【icpc2019网络赛南昌站】Yukino With Subinterval
傻b错误调一天系列
原题:
大意:给你一个数列a,字词两种操作:
1.把c[l]改成r
2.询问在区间[l,r]中,有多少个极大子区间满足子区间里的数全部一样,且在[x,y]范围内
(对于满足条件的区间A,若不存在满足条件的区间B使得A包含于B,则称A为极大子区间)
序列问题,要求复杂度O(nlogn),联想cdq分治
值域可以容斥拆成[1,l-1]和[1,r]两个询问,即把询问转化为区间中数小于等于x的数有多少个
可以把初始数列看成0,然后用修改操作代替初始数列
那么现在就存在偏序:若修改A的时间小于询问B,且A的值小于B的值,则A可以给B提供贡献
对于计算贡献,我们建一个线段树,字词单点修改,并查询有多少个不同的非0极大子区间,这个比较好写
初始按时间排序,然后按值域分治
然后这题就做完了马
反例:
6 3
1 1 4 5 1 4
2 1 6 2 5
1 6 1
2 1 6 2 5
上面的做法会输出3 3,正确答案却是3 2
考试的时候我写这个做法然后深度自闭
出错的原因是因为我们按值域分治,那么当第6个数(被视为操作1 6 4)和第三个操作(2 1 6 2 5)分到一个分治区间时,因为操作2(1 6 1)被分到左边的分治区间了,所以不会把代表第6个数的操作覆盖掉,这时第6个数就给第3个操作产生了贡献(尽管第三个操作进行时它已经被覆盖掉了)
更换提供贡献的顺序是不行的,这是个死循环,不管是用前序、中序或后序cdq分治都无法解决,根本原因是操作2(1 6 1)实际上给操作3(2 1 6 2 5)一个负贡献(它把一个本来合法的区间变得不合法),却因为值域没有达到操作3的范围([2,5])而被忽略了
这个负贡献在分治过程中无法统计(至少我没找到方法)
正确做法是
只考虑一个区间中的左端点那个数,新建一个数组b,对于每个b[i],若a[i]==a[i-1]则b[i]为0否则为a[i]
问题就转化为求区间[l+1,r]中有多少个数,最后单独特判a[l]是否在区间[x,y]内
这个就是经典cdq分治问题,由于数的值域为[1,n],所以用权值线段树(树状数组)可以方便地统计贡献
这种做法和上一种的根本区别在于一个修改操作是覆盖,而另一个是增添和删除,在分治中第二种统计贡献比第一种简单直接很多
然后我就迎来了这题第二次自闭,在晚上10:30写出正确代码后找bug到11:30,直至次日下午5:00才发现问题
只因为一句话的位置:
这是对操作1的处理,注释代码为原位置
如果把d[i]赋值写在后边,就会导致当r==c[l]时,d[i]={0,0,0,0,0,0,0},而我在判断是否应该输出的时候是靠d[i].mk是否为1来判断的
这就导致当r==c[l]发生时,会有修改操作被当成查询操作多输出一个数,自然会WA
这个bug我花了这么长时间没找出来,一个很重要的原因时总在原地打转,算法基本框架检查过很多遍没有问题了,就应该去思考一些细节,尤其是看起来很奇怪很特殊的部分
我倒是看了细节,但是偏偏没有注意到这么奇怪而特殊的一个特判
这个故事也告诉我们有时候睡一觉休息一下bug就自己出来了,不能盲目死磕
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 int rd(){int z=0,mk=1; char ch=getchar(); 8 while(ch<'0'||ch>'9'){if(ch=='-')mk=-1; ch=getchar();} 9 while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0'; ch=getchar();} 10 return z*mk; 11 } 12 struct nds{int mk,x,y,z,id,ans;}a[810000]; int atp=0; 13 int n,m; 14 int b[210000],c[210000]; 15 nds q[810000]; 16 int v[810000]; 17 int ans[210000]; 18 nds d[210000]; 19 inline int lbt(int x){ return x&-x;} 20 void mdf(int x,int y){ for(;x<=n;x+=lbt(x)) v[x]+=y;} 21 int qry(int x){ int bwl=0; for(;x;x-=lbt(x)) bwl+=v[x]; return bwl;} 22 void cdq(int x,int y,int l,int r){ 23 if(x>=y) return ; 24 if(l>r) return ; 25 if(l==r){ 26 for(int i=x;i<=y;++i){ 27 if(!a[i].mk) mdf(a[i].y,a[i].z); 28 else a[i].ans+=qry(a[i].z)-qry(a[i].y-1); 29 } 30 for(int i=x;i<=y;++i)if(!a[i].mk) mdf(a[i].y,-a[i].z); 31 return ; 32 } 33 int md=(l+r)>>1; 34 int cnt1=0; 35 for(int i=x;i<=y;++i){ 36 if(a[i].x<=md) ++cnt1; 37 if(!a[i].mk && a[i].x<=md) mdf(a[i].y,a[i].z); 38 if(a[i].mk && a[i].x>md) a[i].ans+=qry(a[i].z)-qry(a[i].y-1); 39 } 40 for(int i=x;i<=y;++i)if(!a[i].mk && a[i].x<=md) 41 mdf(a[i].y,-a[i].z); 42 int hd1=x,hd2=x+cnt1; 43 for(int i=x;i<=y;++i){ 44 if(a[i].x<=md) q[hd1++]=a[i]; 45 else q[hd2++]=a[i]; 46 } 47 for(int i=x;i<=y;++i) a[i]=q[i]; 48 cdq(x,hd1-1,l,md),cdq(hd1,y,md+1,r); 49 } 50 void prvs(){ 51 atp=0; 52 for(int i=1;i<=m;++i) ans[i]=0; 53 } 54 int main(){ 55 //freopen("ddd.in","r",stdin); 56 cin>>n>>m; 57 prvs(); 58 for(int i=1;i<=n;++i) c[i]=rd(); 59 c[0]=0,c[n+1]=0; 60 for(int i=1;i<=n;++i){ 61 if(c[i]!=c[i-1]) a[++atp]=(nds){0,i,c[i],1,-1,0}; 62 b[i]=c[i]; 63 } 64 int mk,l,r,ql,qr; 65 for(int i=1;i<=m;++i){ 66 mk=rd(); 67 if(mk==1){ 68 l=rd(),r=rd(); 69 d[i]=(nds){mk,l,r,0,0,i}; 70 if(r==c[l]) continue; 71 if(c[l]!=c[l-1]) a[++atp]=(nds){0,l,c[l],-1,i,0}; 72 if(r!=c[l-1]) a[++atp]=(nds){0,l,r,1,i,0}; 73 if(l<n){ 74 if(r==c[l+1]) a[++atp]=(nds){0,l+1,c[l+1],-1,i,0}; 75 if(c[l]==c[l+1]) a[++atp]=(nds){0,l+1,c[l+1],1,i,0}; 76 } 77 c[l]=r; 78 //d[i]=(nds){mk,l,r,0,0,i}; Attention!!! 79 } 80 else{ 81 l=rd(),r=rd(),ql=rd(),qr=rd(); 82 a[++atp]=(nds){1,r,ql,qr,i,0}; 83 a[++atp]=(nds){-1,l,ql,qr,i,0}; 84 d[i]=(nds){mk,l,r,ql,qr,i}; 85 } 86 } 87 cdq(1,atp,1,n); 88 for(int i=1;i<=atp;++i)if(a[i].mk) ans[a[i].id]+=a[i].mk*a[i].ans; 89 for(int i=1;i<=m;++i){ 90 if(d[i].mk==1) b[d[i].x]=d[i].y; 91 else printf("%d\n",ans[i]+(b[d[i].x]>=d[i].z && b[d[i].x]<=d[i].id)); 92 } 93 return 0; 94 }