[CSP-S模拟测试]:联(小清新线段树)

题目描述

由于出题人懒所以没有背景。
一个无限长的$01$序列,初始全为$0$,每次选择一个区间$[l,r]$进行操作,有三种操作:
$\bullet 1\ l\ r$将$[l,r]$中所有元素变成$1$。
$\bullet 2\ l\ r$将$[l,r]$中所有元素变成$0$。
$\bullet 3\ l\ r$将$[l,r]$中所有元素异或上$1$。
每次操作后询问最左边的$0$在哪个位置。


输入格式

第一行一个数$m$,表示序列长度和操作数量。
接下来$m$行,每行三个数$ty\ l\ r$,描述一次操作。


输出格式

输出共$m$行,第$i$行输出一个数表示第$i$次操作后的答案。


样例

样例输入:

3
1 3 4
3 1 6
2 1 3

样例输出:

1
3
1


数据范围与提示

令$n$为$\max(r)$。
对于测试点$1\sim 4$:$n,m\leqslant 10^3$。
对于测试点$5\sim 6$:只有$1$操作。
对于测试点$7\sim 10$:只有$1,2$操作。
对于测试点$11\sim 15$:$n\leqslant 10^5$。
对于测试点$16\sim 20$:无特殊限制。
对于所有的数据,$n\leqslant 10^{18},m\leqslant 10^5$。


题解

看数据范围,肯定是要离散化的,但是离散化的时候需要注意还要将$l+1$和$r+1$离散。

对于只有操作$1,2$的情况,我们可以用线段树直接维护。

那么考虑情况$3$我们可以怎么处理。

如果一段区间都是一样的,我们可以直接将其翻转,然后$return$。

不过这样做可以被很轻松的卡掉,比方说序列是$0101010......$,每次都执行操作$3$,那么会被卡成$n^2$。

但是对于这到柯朵莉树都能$A$掉的题,这就无关紧要了。

时间复杂度:$\Theta(n)\sim \Theta(n^2)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
#define L(x) x<<1
#define R(x) x<<1|1
#define inf 0x3f3f3f3f
using namespace std;
map<long long,long long> mp;
struct rec{int ty;long long l,r;}e[200001];
int m;
int n;
long long pre[5000000];
long long trsam[10000000],trans[10000000],lz[10000000];
void pushup(int x)
{
	trans[x]=min(trans[L(x)],trans[R(x)]);
	trsam[x]=(trsam[L(x)]==trsam[R(x)])?trsam[L(x)]:-1;
}
void pushdown(int x,int l,int r)
{
	if(lz[x]==-1)return;
	int mid=(l+r)>>1;
	lz[L(x)]=lz[R(x)]=trsam[L(x)]=trsam[R(x)]=lz[x];
	if(!trsam[L(x)])trans[L(x)]=l;
	else trans[L(x)]=inf;
	if(!trsam[R(x)])trans[R(x)]=mid+1;
	else trans[R(x)]=inf;
	lz[x]=-1;
}
void build(int x,int l,int r)
{
	trans[x]=inf;
	lz[x]=-1;
	if(l==r)
	{
		trans[x]=l;
		return;
	}
	int mid=(l+r)>>1;
	build(L(x),l,mid);
	build(R(x),mid+1,r);
	pushup(x);
}
void change(int x,int l,int r,int L,int R,int opt)
{
	if(R<l||r<L)return;
	if(L<=l&&r<=R&&trsam[x]!=-1)
	{
		switch(opt)
		{
			case 1:trsam[x]=1;lz[x]=1;break;
			case 2:trsam[x]=0;lz[x]=0;break;
			case 3:trsam[x]^=1;lz[x]=trsam[x];break;
		}
		if(!trsam[x])trans[x]=l;
		else trans[x]=inf;
		return;
	}
	int mid=(l+r)>>1;
	pushdown(x,l,r);
	change(L(x),l,mid,L,R,opt);
	change(R(x),mid+1,r,L,R,opt);
	pushup(x);
}
int main()
{
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
		scanf("%d%lld%lld",&e[i].ty,&e[i].l,&e[i].r);
	for(int i=1;i<=m;++i)
	{
		pre[i*4-3]=e[i].l, pre[i*4-2]=e[i].r;
		pre[i*4-1]=e[i].l+1, pre[i*4]=e[i].r+1;
	}
	pre[m*4+1]=1;
	sort(pre+1,pre+m*4+2);
	n=unique(pre+1,pre+m*4+2)-pre-1;
	for(int i=1;i<=m;++i)
	{
		mp[lower_bound(pre+1,pre+n+1,e[i].r+1)-pre]=e[i].r+1;
		mp[lower_bound(pre+1,pre+n+1,e[i].l+1)-pre]=e[i].l+1;
		long long now=e[i].l;
		e[i].l=lower_bound(pre+1,pre+n+1,e[i].l)-pre;
		mp[e[i].l]=now;
		now=e[i].r;
		e[i].r=lower_bound(pre+1,pre+n+1,e[i].r)-pre;
		mp[e[i].r]=now;
	}
	mp[1]=1;
	build(1,1,n);
	for(int i=1;i<=m;++i)
	{
		change(1,1,n,e[i].l,e[i].r,e[i].ty);
		printf("%lld\n",mp[trans[1]]);
	}
	return 0;
}


rp++

原文地址:https://www.cnblogs.com/wzc521/p/11615873.html

时间: 2024-10-06 17:09:46

[CSP-S模拟测试]:联(小清新线段树)的相关文章

花神游历各国 题解(小清新线段树/树状数组+并查集)

众所周知,这是一道小清新线段树 然而可以用树状数组水过去且跑得飞快 看到区间开方第一反应肯定是线段树懒标记区间修改之类的,但是这个东西似乎确凿不可维护 所以考虑暴力循环单点修改->T飞 于是我们关注一下开方本身的特殊性 我们知道,如果每次向下取整,一个数经过多次操作最终会变成1(或0) 事实上,大概经过 log(logx)次就会变成1 这是什么概念呢?经过博主测试,1e9只要经过五次开方取整就会变成1 那么接下来就能够利用1每次不必再操作优化复杂度 可以维护一个类似链表的结构,指向下一个>1的

[HDU5360]:Gorgeous Sequence(小清新线段树)

题目传送门 题目描述: (原题英文) 操作0:输入l,r,t,线段树区间与t取min. 操作1:输入l,r,区间取最大值. 操作2:输入l,r,区间求和. 样例: 样例输入: 15 51 2 3 4 51 1 52 1 50 3 5 31 1 52 1 5 样例输出: 515312 数据范围与提示: 1≤n,m≤106,0≤ai,t≤231 题解: 很显然区间取最大值和区间求和操作都是线段树基本操作,在此就不做过多赘述. 但是区间与t取最小值很难处理,不用懒标志之类的东西会被卡成n2,那么我们需

[CSP-S模拟测试]:Dash Speed(线段树+并查集+LCA)

题目描述 比特山是比特镇的飙车圣地.在比特山上一共有$n$个广场,编号依次为$1$到$n$,这些广场之间通过$n−1$条双向车道直接或间接地连接在一起,形成了一棵树的结构. 因为每条车道的修建时间以及建筑材料都不尽相同,所以可以用两个数字$l_i,r_i$量化地表示一条车道的承受区间,只有当汽车以不小于$l_i$且不大于$r_i$的速度经过这条车道时,才不会对路面造成伤害. $Byteasar$最近新买了一辆跑车,他想在比特山飙一次车.$Byteasar$计划选择两个不同的点$S,T$,然后在它

[CSP-S模拟测试]:Race(数学+Trie树)

题目描述 一年一度的运动会开始了.有$N$个选手参赛,第$i$个选手有一个能力值(保证$A[i]$两两不同),比赛一共进行了天.在第$j$天($0\leqslant j\leqslant 2^{m-1}$)的比赛中,第$i$个选手的得分为$A[i]\ xor\ j$,然后从大到小排名,排名为$x$($x$从$0$开始)的同学会获得的积分,你需要求出每个同学最后总的积分和$q[i]$模${10}^9+7$的结果$p[i]$.为了避免输出文件过大,你只要输出$p[i]$的异或和即可. 输入格式 第一

【模板】吉司机线段树 HDU 5306 Gorgeous Sequence

也叫小清新线段树,用于解决区间最值修改问题 具体可以参考jiry_2神犇的集训队论文和WC2016上的PPT 此题就作为模板好了,模板的话写法是比较精妙的 #include<bits/stdc++.h> using namespace std; #define go(i,a,b) for(int i=a;i<=b;++i) #define com(i,a,b) for(int i=a;i>=b;--i) #define mem(a,b) memset(a,b,sizeof(a))

CodeForces - 1263E(线段树维护前缀和最值)

题意 https://vjudge.net/problem/CodeForces-1263E 您要设计一个只有一行的打字机,这一行的长度是无限大,一开始可以认为每个字符都是空.您的打字机有一个光标只指向一个字符,一开始指向最左侧的字符. 使用者有三种操作: L 将光标向左移一格(当光标已经在最左侧时,忽略这次操作) R 将光标向右移一格 一个小写字符或者'(',')' 将当前字符替换为给定字符 您需要在每次操作后,判断这一行是否是合法括号序列(例如 (ahakioi) 就是合法的,(ige))(

BZOJ 3779 重组病毒 LCT+线段树维护DFS序

题目大意:给定一棵树,初始每个点都有一个颜色,支持三种操作: 1.将某个点到根的路径上所有点染上一种新的颜色 2.将某个点到根的路径上所有点染上一种新的颜色,然后把根设为这个点 3.定义一个点的代价为这个点到根路径上颜色的种类数,求某个点子树中所有点代价的平均值 我真是炖了狗了-- 容易发现这玩应就是个LCT,操作1就是Access,操作2就是Move_To_Root,代价就是一个点到根路径上的虚边数量+1 我们用LCT模拟上述操作,用线段树维护DFS序维护信息,一旦LCT中出现了虚实边的切换,

Codeforces Beta Round #12 D. Ball (线段树)

题目大意: n个女性中,如果有一个女性的三维比这个女性的三维都大,这个女性就要自杀.问要自杀多少个. 思路分析: 先按照第一维排序. 然后离散化第二维,用第二维的下标建树,树上的值是第三维,更新的时候取最大值. 注意是按照第一维度从大到小进入线段树. 而且还要严格递增. 所以处理第一维度比较大小的时候要分开处理,要把相同的先判断,再更新入树. 那么如何判断这个女性是否自杀. 首先,你知道第一维度是从大到小的,所以先进树了的节点的第一维度一定更大. 再次,我们考虑第二维度,我们去树上第二维度下标大

【10.7校内测试】【队列滑窗】【2-sat】【贪心+栈二分+线段树(noip模拟好题)】【生日祭!】

比较好想的一道题,直接用队列滑窗,因为扫一遍往队列里加东西时,改变的只有一个值,开桶储存好就行了! #include<bits/stdc++.h> using namespace std; int n, k, r; inline int min(int a, int b) { return a > b ? b : a; } inline int max(int a, int b) { return a > b ? a : b; } int sum[200005], q[200005