BZOJ3821 : 玄学
对操作建立线段树,每个节点维护一个有序的操作表,表示用$[l,r]$的操作在每段区间上的作用效果。
对于一个线段树节点,合并左右儿子信息只在该区间刚刚被填满时进行,利用归并排序,时间复杂度为$O(n\log n)$。
查询的时候在线段树上分裂成$O(\log n)$个点,在每个点的表里进行二分查找即可,时间复杂度$O(\log^2n)$每次操作,常数很小。
#include<cstdio> const int N=100000,M=262150,BUF=35000000,OUT=7000000; int Type,n,P,m,co,i,a[N+5],op,A,B,C,D,ans,L[M],R[M],cnt; struct info{ int r,a,b; info(){a=1,b=0;} info(int _r,int _a,int _b){r=_r,a=_a,b=_b;} info operator+(const info&x){return info(r<x.r?r:x.r,1LL*a*x.a%P,(1LL*b*x.a+x.b)%P);} }t[3000000]; void add(int x,int a,int b){ if(a==b){ L[x]=cnt+1; if(A>1)t[++cnt]=info(A-1,1,0); t[++cnt]=info(B,C,D); if(B<n)t[++cnt]=info(n,1,0); R[x]=cnt; return; } int mid=(a+b)>>1; if(m<=mid)add(x<<1,a,mid);else add(x<<1|1,mid+1,b); if(b!=m)return; L[x]=cnt+1; int i=L[x<<1],j=L[x<<1|1],k=R[x<<1],l=R[x<<1|1]; while(i<=k&&j<=l){ t[++cnt]=t[i]+t[j]; if(t[i].r==t[j].r)i++,j++; else if(t[i].r<t[j].r)i++; else j++; } while(i<=k)t[++cnt]=t[i++]; while(j<=l)t[++cnt]=t[j++]; R[x]=cnt; } inline void ask(int x){ int l=L[x],r=R[x],mid,o; while(l<=r)if(t[mid=(l+r)>>1].r>=C)r=(o=mid)-1;else l=mid+1; ans=(1LL*ans*t[o].a+t[o].b)%P; } void query(int x,int a,int b){ if(A<=a&&b<=B){ask(x);return;} int mid=(a+b)>>1; if(A<=mid)query(x<<1,a,mid); if(B>mid)query(x<<1|1,mid+1,b); } char Buf[BUF],*buf=Buf,Out[OUT],*ou=Out;int Outn[30],Outcnt; inline void read(int&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;} inline void write(int x){ if(!x)*ou++=48; else{ for(Outcnt=0;x;x/=10)Outn[++Outcnt]=x%10+48; while(Outcnt)*ou++=Outn[Outcnt--]; } *ou++='\n'; } int main(){ fread(Buf,1,BUF,stdin),read(Type),read(n),read(P),Type&=1; for(i=1;i<=n;i++)read(a[i]); read(co); while(co--){ read(op),read(A),read(B),read(C); if(Type)A^=ans,B^=ans; if(op==1)read(D),m++,add(1,1,N); else{ if(Type)C^=ans; ans=a[C]; query(1,1,N); write(ans); } } fwrite(Out,1,ou-Out,stdout); return 0; }