您的位置:首页 > 其它

BZOJ3821 : 玄学

2015-12-11 02:29 274 查看
对操作建立线段树,每个节点维护一个有序的操作表,表示用$[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;
}


  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: