来北京随便做做题

CF680D *2400

呃这题真的搞了好久啊,理解能力变差了。

我们假设我们最大可以走到的下标是 \(x_i\),最小可以走到的下标是 \(x_j\)。那么答案就是 \(x_i-x_j+1\)

而我们发现这个下标一定是累计得到的,就比如第 \(3\) 时刻的下标一定是 \(a_1+a_2+a_3\),也就是前缀和。所以在不考虑往 \(0\) 的地方填数的情况下,答案就是 \(\max\{\max\{s\}-\min\{s\}+1\}\)

回到我们一开的假设,根据那个假设,我们可以把这只狗行走的部分分成三段。我们不妨设这只狗先走到最小的下标再走到最大的下标,反之亦然。

  • 第一部分,\([1,x_{i-1}]\) 。这一段是这只狗从起点走到最小的下标的过程。

  • 第二部分,\([1,x_j]\) 。这一段是这只狗从起点最大的下标的过程。

  • 第三部分,\([1,n]\) 。这一段是这只狗经过了前面一系列移动最后又回到零点的过程。

那么我们发现,真正影响答案的就是 \([x_{i-1},x_j]\)。这一部分向扩展的越多,那么我们答案也就越大。

考虑到 \(n\le 3000\) ,那我们不妨 \(O(n^2)\) 枚举 \(x_i\)\(x_j\)

我们假设当前这段区间 \([x_i,x_j]\) 已知能扩展的和是 \(\text{sum}\)\(0\) 的个数是 \(\text{zero}\) 那么这段区间最多能扩展到的地方是 \(\text{sum}+\text{zero}\times k\)

但我们必须要明白一点,就是我们扩展到了最大值有些时候是无法回到零点的。所以我们同时也要算出这段区间外的两个区间总共走了多少,给我们留下了多少的走动空间,

我们不妨调整一下顺序,把 \([x_{i-1},x_j]\) 单独考虑。剩下两断合并到一起走,这样显然不影响结果。

那我们同样假设这两段已经扩展的和是 \(\text{sum}\)\(0\) 的个数是 \(\text{zero}\)

那么这一段留给 \([x_{i-1},x_j]\) 空余能走的地方是 \(\text{sum}-\text{zero}\times k\) 。至于为什么这里是减号,还记得我们一开始做的假设吗?我们设这只狗先走到最小的下标再走到最大的下标,所以 \([x_{i-1},x_j]\) 这一段一定是全部向右走,所以剩下两断区间合并起来就是全部向左走,因为最后要回到 \(0\)

最后说一下无解的判断,如果这个数列 \(|s_n|\) 的和大于零的个数乘以 \(k\) 的话,那么必然无解,因为这样就算零的部分全填 \(k\) (或 \(-k\)),都无法达到
\(|s_n|\)

看看关键代码。

	//sum 就是前缀和数组,d 是零的个数
	int tmp=sum[r]-sum[l-1];int cnt=d[r]-d[l-1];//这个是算[x_{i-1},x_j] 这个区间的和以及0的个数
	int tmpp=sum[n]-tmp;int cntt=d[n]-cnt;//剩下两个区间
	ans=max(ans,min(abs(tmp+cnt*k),abs(tmpp-cntt*k)));//先走到最小值再走到最大值
	ans=max(ans,min(abs(tmp-cnt*k),abs(tmpp+cntt*k)));//先走到最大值再走到最小值

CF1746F Kazaee *2800

我靠,难死我了。最后被 rand 卡了几个小时。

具体思路很有趣,xor hash,不过严格意义上这个是 sum hash。

这题题解太多了就不交了。

就是我们发现如果一段区间内每个数出现的次数都是 \(k\) 的正整数倍,那么这一段区间的和一定是 \(k\) 的倍数。

所以我们可以每次给 \(a_i\) 随机加上一个权值 \(w_i\) 然后每一次判断一下这一段的和是不是 \(k\) 是不是 \(k\) 的倍数。

具体地,我们使用卡时的方法,重复随机权值并计算。对于每次询问,只要有一次计算出不满足条件就输出 NO。

就好了。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=7e5+10;
const int mod=1004535809;
int a[N],c[N],tmp[N],l[N],r[N],k[N],rd[N],lsh[N];
int n,sz,q;
bool ans[N];
mt19937 rnd(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
int randint(int L, int R) {
	uniform_int_distribution<int> dist(L, R);
	return dist(rnd);
}
int lowbit(int x){return x&-x;}
void add(int x,int k){
	while(x<=n){
		c[x]+=k;
		x+=lowbit(x);
	}
}
int gsum(int x){
	int sum=0;
	while(x){
		sum+=c[x];
		x-=lowbit(x);
	}
	return sum;
}
int query(int l,int r){
	return gsum(r)-gsum(l-1);
}
signed main(){
	srand(time(0));
	cin>>n>>q;for(int i=1;i<=n;i++){cin>>a[i];lsh[++sz]=a[i];}
	for(int i=1;i<=q;i++){
		int opt;cin>>opt;
		if(opt==1) {cin>>l[i]>>r[i];lsh[++sz]=r[i];}
		else{cin>>l[i]>>r[i]>>k[i];}
	}
	sort(lsh+1,lsh+1+sz);
	sz=unique(lsh+1,lsh+1+sz)-lsh-1;
	for(int i=1;i<=n;i++){a[i]=lower_bound(lsh+1,lsh+1+sz,a[i])-lsh;}
	for(int i=1;i<=q;i++){
		if(k[i]==0){
			r[i]=lower_bound(lsh+1,lsh+1+sz,r[i])-lsh;
		}
	}
	while(clock()<CLOCKS_PER_SEC*2.8){
		for(int i=1;i<=sz;i++) rd[i]=randint(0, mod-1);
		for(int i=1;i<=n;i++) c[i]=0;
		for(int i=1;i<=n;i++) tmp[i]=rd[a[i]];
	 	for(int i=1;i<=n;i++) add(i,tmp[i]);
	 	for(int i=1;i<=q;i++){
	 		if(k[i]==0) {
	 			add(l[i],rd[r[i]]-tmp[l[i]]);
	 			tmp[l[i]]=rd[r[i]];
                                //呃原理大概是 l[i]=tmp[l[i]] 所以 rd[r[i]]-tmp[l[i]]+tmp[l[i]]=rd[r[i]] 所以实现了改变?
	 		}
	 		else{
				if((r[i]-l[i]+1)%k[i]) {ans[i]=true;continue;}
	 			if(query(l[i],r[i])%k[i]) ans[i]=true;
	 		}
	 	}
	}
	for(int i=1;i<=q;i++) {if(k[i]){if(ans[i]==1) puts("NO");else puts("YES");}}
	return 0;
}

CF1646 Playing Around the Table *2900

讲真这题挺简单的,就是想到中间状态很难。

牛逼题。

1:1 2 3 4

2:2 3 4 1

3:3 4 1 2

4:4 1 2 3

我们发现,如果要是当前情况变成了这样,那么是一定可以用 \(\frac{n \times (n-1)}{2}\) 步调整完毕。

具体地,循环移位,好像解释起来很困难。放一个中间结果理解一下 n=5。

点击查看代码
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5


1 1 3 4 5
1 2 2 4 5
1 2 3 3 5
1 2 3 4 4
2 3 4 5 5

__________________

1 1 2 4 5
1 2 2 3 5
1 2 3 3 4
2 3 4 4 5
1 3 4 5 5


1 1 1 4 5
1 2 2 2 5
1 2 3 3 3
2 3 4 4 4
3 4 5 5 5

__________________

1 1 1 3 5
1 2 2 2 4
2 3 3 3 5
1 3 4 4 4
2 4 5 5 5


1 1 1 2 5
1 2 2 2 3
2 3 3 3 4
3 4 4 4 5
1 4 5 5 5


1 1 1 1 5
1 2 2 2 2
2 3 3 3 3
3 4 4 4 4
4 5 5 5 5

__________________

1 1 1 1 4
2 2 2 2 5
1 3 3 3 3
2 4 4 4 4
3 5 5 5 5


1 1 1 1 3
2 2 2 2 4
3 3 3 3 5
1 4 4 4 4
2 5 5 5 5


1 1 1 1 2
2 2 2 2 3
3 3 3 3 4 
4 4 4 4 5
1 5 5 5 5


1 1 1 1 1
2 2 2 2 2
3 3 3 3 3
4 4 4 4 4
5 5 5 5 5

__________________

这个状态我们称之为,中间态,那么如何调整成中间态呢。

注意,因为每个人手中的牌顺序不分先后,所以变成中间态后我们可以全部都当成由小到大排序处理,方便理解。

因此,我们只需要将每一个人手里的牌变成 \(1 \to n\) 即可。方法很弱智。

每次找到第一个有重复元素的人,然后把重复的向下传递,下一个人如果有重复的就传递重复的,没有就把上一个人传递过来的传递到下一个人,以此类推。

这样必然可以在有限步内完成传递。可以证明这个步数是 \(\frac{n \times (n-1)}{2}\),但具体证明我也不太会所以先咕着。

然后这题代码感觉很有难度,稍微写点注释。

int check(){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(cnt[i][j]>1) return i; //如果有重复的就返回当前人,这就找到了第一个有重复元素的人
		}
	}
	return 0;
}
void pre(){//变为目标状态
	while(1){
		int nwep=check();int las=0;//las 是当前传递的元素
		if(nwep==0) break;//如果没有找到有重复的就已经是中间态了,break出来
		sz++;//这是一次操作
		for(int j=1;j<=n;j++){
			if(cnt[nwep][j]>1){//找到重复元素
				cnt[nwep][j]--;//因为要传递下去,所以个数减一
				ans[sz][nwep]=las=j;//ans[i][j]表示第i步第j个人传递的数
                                //这里只给cnt[nwep][j]--的原因是你只知道要传递给谁但不知道谁传递给他,因为要绕一周才能传递回来
				break;
			}
		}
		for(int i=nwep%n+1;i!=nwep;i=i%n+1){//这样就很好的完成了一周的传递,技巧+1,这个枚举的是人
			bool flag=0;
			for(int j=1;j<=n;j++){
				if(cnt[i][j]>1){//如果当前这个元素重复
					cnt[i][j]--;cnt[i][las]++;//这里可以直接加是因为不用绕一圈,直接传过来了
					ans[sz][i]=las=j;//更新答案和接下来要继续传递的值		
					flag=1;
					break;
				}
			}
			if(flag==0) ans[sz][i]=las;//如果一次都没更新过证明当前人已经是目标状态了,把前面那个人船体过来的东西原封不动传递下去
		}
		cnt[nwep][las]++;//此时已经转了一圈回来了,所以更新第一个人的
	}
}
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			int x;cin>>x;cnt[i][x]++;//提前记录出现次数
		}
	}
	pre();
	for(int i=2;i<=n;i++){//这里要开始传递了,这里是枚举第一个移动哪个,参考上面例子,语言解释不清楚了(
		for(int j=1;j<i;j++){
			sz++;//一步
			for(int k=1;k<=n;k++){
				int val=(k+i-j+n)%n;//这里就是阴间循环移位,k+i-j+n 对应了当前移动哪个
				if(val==0) val=n;
				ans[sz][k]=val;//改答案
                                //按理来讲这里还要改一下那个桶数组,不过因为不会用到就去掉了
			}
		}
	}
	cout<<sz<<"\n";
	for(int i=1;i<=sz;i++){
		for(int j=1;j<=n;j++){
			// if(ans[i][j]==0) cout<<"worng: "<<i<<endl;
			cout<<ans[i][j]<<" ";//输出
		}
		cout<<"\n";
	}
	return 0;
}

CF1646E Power Board *2200

呃蛮简单题。

第一行单独处理。

我们考虑如果第 \(i\) 行的 \(i\) 可以被表示为 \(d^k\) 那么他就有可能重复。考虑如果 \(d\) 不一样的话一定不会有重复,所以我们单独考虑 \(k\)

显然 \(k\) 的上限是 \(\log_d^n\)

我们发现对于一个 \(i\) ,他给答案的贡献是 \(1\times 1, 1\times 2....1\times m, 2 \times 1, 2\times 2.....2\times m .....k\times 1....k\times m\) 中不同数的个数。

考虑到 \(k\) 的最大值是 \(20\),所以我们可以直接枚举 \(k\)\(m\) 然后开个桶来预处理出每个 \(k\) 的贡献。

计算的时候我们对于每一个 \(i\) 求出他的 \(k\),然后把所有 \(\le n\) 并可以表示为 \(i^x\) 的数标记掉,因为我们刚刚在计算 \(i\) 的时候已经把他贡献算过了。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+1e4;
bool vis[N*20];
int viss[N],cnt,ans[N],sum;
int n,m;
signed main(){
	cin>>n>>m;
	for(int i=1;i<=20;i++){
		for(int j=1;j<=m;j++){
			if(vis[i*j]==0){
				vis[i*j]=1;
				cnt++;
			}
		}
		ans[i]=cnt;
		// cout<<ans[i]<<" ";
	}
	// cout<<endl;
	for(int i=2;i<=n;i++){
		if(viss[i]==1) continue;
		int tmp=i,po=0;
		while(tmp<=n){
			// cout<<tmp<<"qwq\n";
			po++;
			// cout<<po<<endl;
//			cout<<tmp<<" "<<i<<" "<<tmp*i<<endl;
			tmp*=i;
			if(tmp>n) break;
//			cout<<tmp<<endl;
			viss[tmp]=1;
		}
		// cout<<tmp<<" "<<po<<endl;
		sum+=ans[po];
	}
	cout<<sum+1;
	return 0;
}

CF1545D AquaMoon and Wrong Coordinate *3000

这题他妈的就不是人能想出来的。

注意以下题解默认初始位置是 \(1\) 时刻。

首先我们确定哪一行出了问题是平凡的。

\(t\) 时刻时,这些点的坐标和应该是 \(\sum_{i=1}^n x_i+t\times v_i\)

\(t+1\) 时刻时,这些点的坐标和应该是 \(\sum_{i=1}^n x_i+(t+1)\times v_i\)

然后把这两个式子相减发现是 \(\sum{i=1}^n v_i\),至于这个 \(v_i\) 是多少并不重要,因为 \(v_i\) 不会变,所以我们把每个时刻的坐标和相减,我们找到 第一个 \(x\) 使得第 \(x\) 行减去第 \(x-1\) 行的值与其他的值不同,那么问题出在第 \(x\) 行上。

确定哪一行,如何确定具体该改多少呢。这里就要用到人类智慧了。

我们考虑记函数 \(f(t)=\sum_{i=1}^m(x_i+t\times v_i)^2\) 即每一行位移的平方和。

我们展开一下 \(f(t+1)\)\(f(t-1)\),发现一个式子。

\(f(t+1)+f(t-1)-2*f(t)=\sum_{i=1}^m 2v_i\)

所以可以得到,任意连续三个时刻的坐标的平方和同样是固定的。

而我们又知道哪一时刻出了问题,所以我们枚举这一时刻的每一个坐标把他改成 \(k\) ,如果哪一次改了之后上式的值与正常的值相等,那么这个位置就要改成 \(k\) ,这题就做完了。

还有几个细节,第一个是应该改成什么。

我们假设两个时刻时间应该差 \(x\) 但实际上差了 \(y\),那么因为我们假设了其他位置没错,所以一定是这个位置的问题,所以说当前坐标应该 \(-y+x\)

然后还有因为我们连续最后一步要连续取三个时刻当作标准值,所以如果问题出在前三个,那么标准值就取 \(t=5,6,7\) 时刻的 \(f(t)\),否则就取前三个。

具体看代码。

	for(int i=2;i<=k;i++){
		if(mp[s[i]-s[i-1]]==1){
			flag=i;
			if(i<=3) flag2=5;
			else flag2=2;
			break;
		}
	}
	cout<<flag-1<<" ";//这里要减一是因为题目默认初始时刻是0,而我是按照1处理的
	int tmp=s[flag2+1]-s[flag2];int tmp2=v[flag2+1]+v[flag2-1]-2*v[flag2];
	for(int i=1;i<=n;i++){
		int ans=pos[flag][i]-(s[flag]-s[flag-1])+tmp;
		// cout<<ans<<" "<<tmp<<" "<<" "<<s[flag]-s[flag-1]<<endl;
		if(v[flag+1]+v[flag-1]-2*(v[flag]+ans*ans-pos[flag][i]*pos[flag][i])==tmp2){
			cout<<ans<<"\n";
			cout.flush();
			return 0;
		}	
	}

CF896C *2600

ODT 模板题。

大概思路就是用一个set来维护,然后就暴力。感觉很神奇,但复杂度为什么是对的我也不知道。

// LUOGU_RID: 103272881
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
const int N=1e5+10;
int ksm(int a,int b,int mod){
	int ret=1;a%=mod;
	while(b){
		if(b&1) ret=ret*a%mod;
		a=a*a%mod;a%=mod;b>>=1;
	}
	return ret%mod;
}
int a[N],n,m,seed,vmax;
int rnd(){
	int ret=seed;
	seed=(seed*7+13)%mod;
	return ret;
}
struct node{
	int l,r;
	mutable int v;
	node(int l,int r=0,int v=0):l(l),r(r),v(v){}
	bool operator <(const node &a) const{
		return l<a.l;
	}
};
set<node> s;
set<node>::iterator split(int pos){
	set<node>::iterator it=s.lower_bound(node(pos));
	// cout<<it->v<<endl;
	if(it!=s.end() and it->l==pos){
		return it;
	}
	it--;if(it->r<pos) return s.end();
	int l=it->l;int r=it->r;int v=it->v;
	s.erase(it);s.insert(node(l,pos-1,v));
	return s.insert(node(pos,r,v)).first;
}
void add(int l,int r,int x){
	// cout<<"qwq\n";
	set<node>::iterator itr=split(r+1),itl=split(l);
	for(set<node>::iterator i=itl;i!=itr;i++){
		i->v+=x;
	}
	// cout<<"qwq\n";
}
void change(int l,int r,int x){
	set<node>::iterator itr=split(r+1),itl=split(l);
	s.erase(itl,itr);
	s.insert(node(l,r,x));
}
struct nodee{
	nodee(){}
	int num,cnt;
	bool operator <(const nodee &a) const{
		return num<a.num;
	}
	nodee(int num,int cnt): num(num),cnt(cnt){}
}rk[N];
int rnk(int l,int r,int x){
	set<node> ::iterator itr=split(r+1),itl=split(l);
	int sz=0;
	for(set<node>::iterator i=itl;i!=itr;i++){
		rk[++sz]=nodee(i->v,i->r-i->l+1);
	}
	sort(rk+1,rk+1+sz);
	int k;
	for(k=1;k<=sz;k++){
		if(x>rk[k].cnt) x-=rk[k].cnt;
		else break;
	}
	return rk[k].num;

}
int calc(int l,int r,int x,int y){
	set<node> ::iterator itr=split(r+1),itl=split(l);
	int ans=0;
	for(set<node>::iterator i=itl;i!=itr;i++){
		ans+=(ksm(i->v,x,y)*(i->r-i->l+1)%y)%y;
		ans%=y;
	}
	return ans;
}
signed main(){
	cin>>n>>m>>seed>>vmax;
	for(int i=1;i<=n;i++){
		a[i]=(rnd()%vmax)+1;
		s.insert(node(i,i,a[i]));
	}
	for(int i=1;i<=m;i++){
		int op=(rnd()%4)+1;
		int l=(rnd()%n)+1;
		int r=(rnd()%n)+1;int x=0,y=0;
		if(l>r) swap(l,r);
		if(op==3) x=(rnd()%(r-l+1))+1;
		else x=(rnd()%vmax)+1;
		if(op==4) y=(rnd()%vmax)+1;
		if(op==1){add(l,r,x);}
		if(op==2){change(l,r,x);}
		if(op==3){cout<<rnk(l,r,x)<<"\n";}
		if(op==4){cout<<calc(l,r,x,y)<<"\n";}
	}
	return 0;
}

把几个用珂朵莉树草过去的给挂着,思路不写,太简单了(

P4344 SHOI2015 脑洞治疗仪 紫

CF817F MEX Queries *2300

P2572 [SCOI2010] 序列操作 紫

思路不难想,类似小白逛公园。维护八个值,0/1 的个数,从左/右边开始的最长连续0/1的个数,整个区间的 0/1 个数,然后直接模仿小白逛公园就可以了。但为啥查询还有push_up啊,迷惑。写了 4.5k,困难的。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct node{
	int w,b,lw,lb,rw,rb,mw,mb,l,r,len,add,add2;//add 区间反转 add2 区间赋值
	// node(int w=0,int b=0,int lw=0,int lb=0,int rw=0,int rb=0,int mw=0,int mb=0,int l=0,int r=0,int len=0,int add=0,int add2=0): 
	// w(w),b(b),lw(lw),lb(lb),rw(rw),rb(rb),mw(mw),mb(mb),l(l),r(r),len(len),add(add),add2(add2){}
}t[N*4];
int n,m,a[N];
void out_put(int p){
	cout<<p<<" "<<t[p].add<<" "<<t[p].w<<" "<<t[p].b<<" "<<t[p].lw<<" "<<t[p].lb<<" "<<t[p].rw<<" "<<t[p].rb<<" "<<t[p].mw<<" "<<t[p].mb<<endl;
}
void push_up(int p){
	// if(p==4){
	// 	out_put(8);out_put(9);
	// }
	t[p].w=t[p*2].w+t[p*2+1].w;t[p].b=t[p*2].b+t[p*2+1].b;
	if(t[p*2].b==0) t[p].lw=(t[p*2].w+t[p*2+1].lw);
	else t[p].lw=t[p*2].lw;
	if(t[p*2].w==0) t[p].lb=(t[p*2].b+t[p*2+1].lb);
	else t[p].lb=t[p*2].lb;
	if(t[p*2+1].b==0) t[p].rw=(t[p*2].rw+t[p*2+1].w);
	else t[p].rw=t[p*2+1].rw;
	if(t[p*2+1].w==0) t[p].rb=(t[p*2].rb+t[p*2+1].b);
	else t[p].rb=t[p*2+1].rb;
	t[p].mw=max(max(t[p*2].mw,t[p*2+1].mw),t[p*2].rw+t[p*2+1].lw);
	t[p].mb=max(max(t[p*2].mb,t[p*2+1].mb),t[p*2].rb+t[p*2+1].lb);
	// if(p==2){cout<<"qwq ";out_put(p);out_put(p*2);out_put(p*4+1);}
}
void build(int l,int r,int p){
	t[p].l=l;t[p].r=r;t[p].len=r-l+1;t[p].add2=-1;
	if(l==r){
		if(a[l]==0){
			t[p].w=0;t[p].b=1;t[p].lw=0;t[p].lb=1;t[p].rw=0;t[p].rb=1;t[p].mw=0;t[p].mb=1;
		}
		else{
			t[p].w=1;t[p].b=0;t[p].lw=1;t[p].lb=0;t[p].rw=1;t[p].rb=0;t[p].mw=1;t[p].mb=0;
		}
		return ;
	}
	int mid=(l+r)/2;
	build(l,mid,p*2);build(mid+1,r,p*2+1);push_up(p);
}
void push_down(int p){
	if(t[p].add2==0){//区间赋0
		t[p*2].add2=0;t[p*2+1].add2=0;t[p*2].add=0;t[p*2+1].add=0;
		t[p*2].w=0;t[p*2].lw=0;t[p*2].rw=0;t[p*2].mw=0;
		t[p*2+1].w=0;t[p*2+1].lw=0;t[p*2+1].rw=0;t[p*2+1].mw=0;
		t[p*2].b=t[p*2].len;t[p*2].lb=t[p*2].len;t[p*2].rb=t[p*2].len;t[p*2].mb=t[p*2].len;
		t[p*2+1].b=t[p*2+1].len;t[p*2+1].lb=t[p*2+1].len;t[p*2+1].rb=t[p*2+1].len;t[p*2+1].mb=t[p*2+1].len;
		t[p].add2=-1;
	}
	if(t[p].add2==1){//区间赋1
		t[p*2].add2=1;t[p*2+1].add2=1;t[p*2].add=0;t[p*2+1].add=0;
		t[p*2].w=t[p*2].len;t[p*2].lw=t[p*2].len;t[p*2].rw=t[p*2].len;t[p*2].mw=t[p*2].len;
		t[p*2+1].w=t[p*2+1].len;t[p*2+1].lw=t[p*2+1].len;t[p*2+1].rw=t[p*2+1].len;t[p*2+1].mw=t[p*2+1].len;
		t[p*2].b=0;t[p*2].lb=0;t[p*2].rb=0;t[p*2].mb=0;
		t[p*2+1].b=0;t[p*2+1].lb=0;t[p*2+1].rb=0;t[p*2+1].mb=0;
		t[p].add2=-1;
	}
	if(t[p].add!=0){//区间反转
		t[p*2].add^=1;t[p*2+1].add^=1;
		swap(t[p*2].w,t[p*2].b);swap(t[p*2].lw,t[p*2].lb);swap(t[p*2].rw,t[p*2].rb);swap(t[p*2].mw,t[p*2].mb);
		swap(t[p*2+1].w,t[p*2+1].b);swap(t[p*2+1].lw,t[p*2+1].lb);swap(t[p*2+1].rw,t[p*2+1].rb);swap(t[p*2+1].mw,t[p*2+1].mb);
		t[p].add=0;
	}
}
void change(int l,int r,int p,int k){
	if(l<=t[p].l and t[p].r<=r){
		if(k==0){
			t[p].add=0;
			t[p].w=0;t[p].lw=0;t[p].rw=0;t[p].mw=0;
			t[p].b=t[p].len;t[p].lb=t[p].len;t[p].rb=t[p].len;t[p].mb=t[p].len;
			t[p].add2=0;
		} 
		if(k==1){
			t[p].add=0;
			t[p].b=0;t[p].lb=0;t[p].rb=0;t[p].mb=0;
			t[p].w=t[p].len;t[p].lw=t[p].len;t[p].rw=t[p].len;t[p].mw=t[p].len;
			t[p].add2=1;
		}
		if(k==2){
			swap(t[p].w,t[p].b);swap(t[p].lw,t[p].lb);swap(t[p].rw,t[p].rb);swap(t[p].mw,t[p].mb);
			t[p].add^=1;
		}
		// cout<<k<<" "<<t[p].l<<" "<<t[p].r<<" "<<t[p].add<<endl;
		// push_down(p);
		// out_put(p);
		// out_put(2);
		return ;
	}
	int mid=(t[p].l+t[p].r)/2;
	push_down(p);push_up(p);
	if(l<=mid) change(l,r,p*2,k);
	if(r>mid) change(l,r,p*2+1,k);
	push_down(p);push_up(p);
}
node query(int l,int r,int p){
	if(l<=t[p].l and t[p].r<=r)	return t[p];
	int mid=(t[p].l+t[p].r)/2;
	push_down(p);push_up(p);
	node ans;
	if(r<=mid)	ans=query(l,r,p*2);
	else if(l>mid)	ans=query(l,r,p*2+1);
	else{
		node tmp=query(l,r,p*2);
		node tmp2=query(l,r,p*2+1);
		ans.w=tmp.w+tmp2.w;
		ans.b=tmp.b+tmp2.b;
		if(tmp.b==0) ans.lw=(tmp.w+tmp2.lw);
		else ans.lw=tmp.lw;
		if(tmp.w==0) ans.lb=(tmp.b+tmp2.lb);
		else ans.lb=tmp.lb;
		if(tmp2.b==0) ans.rw=(tmp.rw+tmp2.w);
		else ans.rw=tmp2.rw;
		if(tmp2.w==0) ans.rb=(tmp.rb+tmp2.b);
		else ans.rb=tmp2.rb;
		ans.mw=max(max(tmp.mw,tmp2.mw),tmp.rw+tmp2.lw);
		ans.mb=max(max(tmp.mb,tmp2.mb),tmp.rb+tmp2.lb);
	}
	push_up(p);
	return ans;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	build(1,n,1);
	while(m--){
		int opt,l,r;cin>>opt>>l>>r;
		l++;r++;
		if(opt==0){
			change(l,r,1,0);
		}
		if(opt==1){
			change(l,r,1,1);
			// cout<<t[2].w<<endl;
		}
		if(opt==2){
			change(l,r,1,2);
		}
		else{
			if(opt==3) cout<<query(l,r,1).w<<"\n";
			else if(opt==4) cout<<query(l,r,1).mw<<"\n";
		}
	}
	return 0;
}

P4979 矿洞:坍塌 蓝

线段树入门题。维护一个颜色就可以了,1/2/3 分别是全A/B/C,-1 表示混色。

最后查询不用直接返回合不合法,如果这个区间是纯色那么返回区间的颜色就可以了,否则返回 -1

判断合法的条件就是 [l,r] 返回值不为 -1 且 [l-1,l-1],[r+1,r+1] 不相同。有两种情况例外,l=1 或者 r=n 。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
char s[N];
int a[N],n,k;
struct node{
	int l,r,col,add;
}t[N*4];
void push_up(int p){
	if(t[p*2].col==t[p*2+1].col) t[p].col=t[p*2].col;
	else t[p].col=-1;
}
void push_down(int p){
	if(t[p].add!=-1){
		t[p*2].col=t[p].add;t[p*2+1].col=t[p].add;
		t[p*2].add=t[p].add;t[p*2+1].add=t[p].add;
		t[p].add=-1;
	}
}
void build(int l,int r,int p){
	t[p].l=l;t[p].r=r;t[p].add=-1;
	if(l==r){
		t[p].col=a[l];
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,p*2);build(mid+1,r,p*2+1);push_up(p);
}
void change(int l,int r,int p,int k){
	// cout<<l<<" "<<r<<" "<<p<<" "<<k<<endl;
	if(l<=t[p].l and t[p].r<=r){
		t[p].add=k;t[p].col=k;
		push_down(p);
		return ;
	}
	push_down(p);
	int mid=(t[p].l+t[p].r)>>1;
	if(l<=mid) change(l,r,p*2,k);
	if(r>mid) change(l,r,p*2+1,k);
	push_up(p);
}
int query(int l,int r,int p){
	if(l==0 or r==n+1) return 0;
	if(l<=t[p].l and t[p].r<=r) return t[p].col;
	int mid=(t[p].l+t[p].r)>>1;
	push_down(p);
	int col1=0,col2=0;
	if(l<=mid) col1=query(l,r,p*2);
	if(r>mid) col2=query(l,r,p*2+1);
	if(col1==0) return col2;
	if(col2==0) return col1;
	if(col1==-1 or col2==-1) return -1;
	if(col1==col2) return col1;
	return -1;
}
int main(){
	cin>>n>>(s+1);
	for(int i=1;i<=n;i++){a[i]=s[i]-'A'+1;}
	build(1,n,1);
	cin>>k;
	while(k--){
		char opt,op;int x,y;
		cin>>opt>>x>>y;
		if(opt=='B'){
			int fr=query(x-1,x-1,1);int bc=query(y+1,y+1,1);
			int ret=query(x,y,1);
			// cout<<ret<<" "<<fr<<" "<<bc<<endl;
			if(ret==-1) cout<<"No\n";
			else if(ret!=-1 and (fr!=bc or x==1 or y==n)) cout<<"Yes\n";
			else cout<<"No\n";
		}
		if(opt=='A'){cin>>op;change(x,y,1,op-'A'+1);}
	}
	return 0;
}
posted @ 2023-02-22 14:54  zplqwq  阅读(72)  评论(0编辑  收藏  举报