BZOJ4447: [Scoi2015]小凸解密码
这题怎么这么难读懂……而且还读错了一次……mhy的游记里写的他写的二分+线段树,然后还说可以用set,表示非常懵逼……我只会线段树暴搞,维护到左右端点的最远和次远的0区间,查询分别查左右两半的答案,再设法合并……跑得巨慢……
UPD:确实用set维护一下所有0区间就好了……我怎么想出来这个线段树的……脑抽系列……
#include<algorithm> #include<cstdio> #define I (J+1) #define J (i+j>>1) #define P (k<<1) #define S (P^1) using namespace std; const int N=1e5+5; int n,m,s,t,a[N]; char c[N]; struct node{ int x,y,z,s1,s2,t1,t2; void pre(int i){ x=y=i,z=1,s1=t1=i?-1:0,s2=t2=-1; } }f[N*8]; node operator+(node a,node b){ node c={a.x,b.y,a.z+b.z}; int j=a.y||b.x?-1:0; if(b.s1==j)c.s1=a.s1,c.s2=a.s2; else c.s1=b.s1+a.z,c.s2=b.s2!=j?b.s2+a.z:a.s1; if(a.t1==j)c.t1=b.t1,c.t2=b.t2; else c.t1=a.t1+b.z,c.t2=a.t2!=j?a.t2+b.z:b.t1; return c; } int cal(int i){ int s=i%n,t=(s+n-1)%n; if(c[s]=='+') return(a[s]+a[t])%10; return(a[s]*a[t])%10; } void pre(int i,int j,int k){ if(i==j)f[k].pre(cal(i)); else pre(i,J,P),pre(I,j,S),f[k]=f[P]+f[S]; } void vary(int s,int t,int i,int j,int k){ if(i==j)f[k].pre(t); else{ if(s<I)vary(s,t,i,J,P); if(s>J)vary(s,t,I,j,S); f[k]=f[P]+f[S]; } } node ask(int s,int t,int i,int j,int k){ if(s==i&&j==t)return f[k]; if(t<I)return ask(s,t,i,J,P); if(s>J)return ask(s,t,I,j,S); return ask(s,J,i,J,P)+ask(I,t,I,j,S); } void vary(int s,int t){vary(s,t,0,n*2-1,1);} node ask(int s,int t){return ask(s,t,0,n*2-1,1);} int main(){ scanf("%d%d",&n,&m); for(int i=0;i<n;++i) scanf("%d %c",a+i,c+i); pre(0,n*2-1,1); while(m--){ scanf("%d%d",&s,&t); if(s==1){ scanf("%d %c",a+t,c+t); vary(t,cal(t)); vary(t+n,cal(t)); vary(t+1,cal(t+1)); if(t!=n-1) vary(t+n+1,cal(t+1)); }else{ vary(t,a[t]); node a=ask(t,t+(n-1)/2),b=ask(t+(n+1)/2,t+n-1); if(~b.t1&&(a.x||b.t1))++b.t1; if(~b.t2&&(a.x||b.t2))++b.t2; printf("%d\n",a.y||b.x?max(a.s1,b.t1):max(max(a.s2,b.t2),min(a.s1,b.t1))); vary(t,cal(t)); } } }