来北京随便做做题
CF680D *2400
呃这题真的搞了好久啊,理解能力变差了。
我们假设我们最大可以走到的下标是 ,最小可以走到的下标是 。那么答案就是 。
而我们发现这个下标一定是累计得到的,就比如第 时刻的下标一定是 ,也就是前缀和。所以在不考虑往 的地方填数的情况下,答案就是 。
回到我们一开的假设,根据那个假设,我们可以把这只狗行走的部分分成三段。我们不妨设这只狗先走到最小的下标再走到最大的下标,反之亦然。
-
第一部分, 。这一段是这只狗从起点走到最小的下标的过程。
-
第二部分, 。这一段是这只狗从起点最大的下标的过程。
-
第三部分, 。这一段是这只狗经过了前面一系列移动最后又回到零点的过程。
那么我们发现,真正影响答案的就是 。这一部分向扩展的越多,那么我们答案也就越大。
考虑到 ,那我们不妨 枚举 和 。
我们假设当前这段区间 已知能扩展的和是 , 的个数是 那么这段区间最多能扩展到的地方是 。
但我们必须要明白一点,就是我们扩展到了最大值有些时候是无法回到零点的。所以我们同时也要算出这段区间外的两个区间总共走了多少,给我们留下了多少的走动空间,
我们不妨调整一下顺序,把 单独考虑。剩下两断合并到一起走,这样显然不影响结果。
那我们同样假设这两段已经扩展的和是 , 的个数是 。
那么这一段留给 空余能走的地方是 。至于为什么这里是减号,还记得我们一开始做的假设吗?我们设这只狗先走到最小的下标再走到最大的下标,所以 这一段一定是全部向右走,所以剩下两断区间合并起来就是全部向左走,因为最后要回到 。
最后说一下无解的判断,如果这个数列 的和大于零的个数乘以 的话,那么必然无解,因为这样就算零的部分全填 (或 ),都无法达到
。
看看关键代码。
//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。
这题题解太多了就不交了。
就是我们发现如果一段区间内每个数出现的次数都是 的正整数倍,那么这一段区间的和一定是 的倍数。
所以我们可以每次给 随机加上一个权值 然后每一次判断一下这一段的和是不是 是不是 的倍数。
具体地,我们使用卡时的方法,重复随机权值并计算。对于每次询问,只要有一次计算出不满足条件就输出 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
我们发现,如果要是当前情况变成了这样,那么是一定可以用 步调整完毕。
具体地,循环移位,好像解释起来很困难。放一个中间结果理解一下 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 __________________
这个状态我们称之为,中间态,那么如何调整成中间态呢。
注意,因为每个人手中的牌顺序不分先后,所以变成中间态后我们可以全部都当成由小到大排序处理,方便理解。
因此,我们只需要将每一个人手里的牌变成 即可。方法很弱智。
每次找到第一个有重复元素的人,然后把重复的向下传递,下一个人如果有重复的就传递重复的,没有就把上一个人传递过来的传递到下一个人,以此类推。
这样必然可以在有限步内完成传递。可以证明这个步数是 ,但具体证明我也不太会所以先咕着。
然后这题代码感觉很有难度,稍微写点注释。
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
呃蛮简单题。
第一行单独处理。
我们考虑如果第 行的 可以被表示为 那么他就有可能重复。考虑如果 不一样的话一定不会有重复,所以我们单独考虑 。
显然 的上限是 。
我们发现对于一个 ,他给答案的贡献是 中不同数的个数。
考虑到 的最大值是 ,所以我们可以直接枚举 和 然后开个桶来预处理出每个 的贡献。
计算的时候我们对于每一个 求出他的 ,然后把所有 并可以表示为 的数标记掉,因为我们刚刚在计算 的时候已经把他贡献算过了。
#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
这题他妈的就不是人能想出来的。
注意以下题解默认初始位置是 时刻。
首先我们确定哪一行出了问题是平凡的。
在 时刻时,这些点的坐标和应该是 。
在 时刻时,这些点的坐标和应该是 。
然后把这两个式子相减发现是 ,至于这个 是多少并不重要,因为 不会变,所以我们把每个时刻的坐标和相减,我们找到 第一个 使得第 行减去第 行的值与其他的值不同,那么问题出在第 行上。
确定哪一行,如何确定具体该改多少呢。这里就要用到人类智慧了。
我们考虑记函数 即每一行位移的平方和。
我们展开一下 与 ,发现一个式子。
。
所以可以得到,任意连续三个时刻的坐标的平方和同样是固定的。
而我们又知道哪一时刻出了问题,所以我们枚举这一时刻的每一个坐标把他改成 ,如果哪一次改了之后上式的值与正常的值相等,那么这个位置就要改成 ,这题就做完了。
还有几个细节,第一个是应该改成什么。
我们假设两个时刻时间应该差 但实际上差了 ,那么因为我们假设了其他位置没错,所以一定是这个位置的问题,所以说当前坐标应该 。
然后还有因为我们连续最后一步要连续取三个时刻当作标准值,所以如果问题出在前三个,那么标准值就取 时刻的 ,否则就取前三个。
具体看代码。
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; }
本文作者:zplqwq
本文链接:https://www.cnblogs.com/zplqwq/p/17144367.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步