- 清华集训2014 玄学
- 一列对象,常见的区间修改,单点询问,修改操作易于复合,但未必交换以及有逆。
- 现在询问的时候忽视掉一段前缀与后缀,只留下中间一段连续的修改
- 强制在线,\(n\leq 10^5,q\leq 6*10^5\),保证修改操作不超过\(10^5\)。
- 二进制分组,猫琨暑假讲过的原题,但是我睡着了。
- 二进制分组是一种策略,应对询问独立的情况,最基本的二进制分组是这样的:
- 刚开始没有分组
- 新来一个修改的时候,对这个修改新建一个分组
- 如果存在两个大小一样的分组,将它们合并,一直执行直到不存在两个分组大小相等。
- 通常用线段树实现。
- 这个模型有很多种等价的变形,比如可以把 \(cdq\) 分治在线化。
但是我不会啊,到时候可能会补一个二进制分组的学习笔记,咕咕。
- 对于这个题,考虑每一次询问是互相独立的。
- 那么对修改操作建时间轴线段树,每一次修改就相当于把一个叶子节点的区间搞一下。
- 对每个节点新建\(vector\)保存这个节点当前的所有区间。
- 假设我们已经快速整理出两个儿子的区间信息,按次序合并在父亲了。
- 那么每一次询问可以定位到线段树上的\(log\)个区间,按照次序依次产生贡献就可以了。
- 这个可以直接在\(vector\)上二分出对应的修改标记。
- 所以查询是两个\(log\)的。
- 现在的问题是怎么把两个儿子快速合并,即把两个\(vector\)合并成一个。
- 这里采用的是归并排序的思想。
- 为了方便,如果两个区间有交,那么就把这两个区间拆成三个区间。
- 因为每新建一个修改只会产生一个区间,所以总共的端点数不会超过\(2*n\),是可以接受的。
- 为了方便,一次修改我们拆开成了三个区间,即右端点分别为\(l-1\),\(r\),\(n\)。
- 这样的好处在于每一个\(vector\)所拥有的区间拼接起来一定是全集。
- 还有一个细节就是不能每次修改都\(pushup\),因为会被不断合并多次,就假了。
- 可以选择如果一个节点的儿子满了,那么就把他\(pushup\)。
- 正确性就是,如果一个节点没有被插满,那么他就一定不会被定位到。
- 因为询问不会询问在这之后的修改操作。
- 这样的话,修改就只有一个\(log\)。
- 而每个节点只会被\(pushup\)一次,所以\(pushup\)的复杂度是线性的。
- 总复杂度两个\(qlog^2q\),而且算法在线。
- 可以无脑树套树,
但是我忘了。
- 疯狂膜拜分块暴切的\(cx233666\)大聚聚。
#include<bits/stdc++.h>
#define R register int
using namespace std;
const int N=100001;
const int M=600000;
const int T=400001;
const int Mx=100000;
int S,n,m,op,l,r,a,b,ans,q,K,tot,w[N];
struct ip{int r,a,b;}nw;
vector<ip>G[T];
int gi(){
R x=0,k=1;char c=getchar();
while(c!='-'&&(c<'0'||c>'9'))c=getchar();
if(c=='-')k=-1,c=getchar();
while(c<='9'&&c>='0')x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x*k;
}
void upd(R i){
R ls=(i<<1),rs=(ls|1),llen=G[ls].size(),rlen=G[rs].size();
G[i].clear();
for(R p=0,q=0;p<llen&&q<rlen;){
R a=1ll*G[ls][p].a*G[rs][q].a%m,b=(1ll*G[rs][q].a*G[ls][p].b+G[rs][q].b)%m;
if(G[ls][p].r==G[rs][q].r)G[i].push_back((ip){G[ls][p].r,a,b}),++p,++q;
else if(G[ls][p].r<G[rs][q].r)G[i].push_back((ip){G[ls][p].r,a,b}),++p;
else G[i].push_back((ip){G[rs][q].r,a,b}),++q;
}
}
int mdf(R Le,R Ri,R i){// tot l r a b
if(Le==Ri){
if(l!=1)G[i].push_back((ip){l-1,1,0});
G[i].push_back((ip){r,a,b});
if(r!=n)G[i].push_back((ip){n,1,0});
return 1;
}
R mid=(Le+Ri)>>1,ls=(i<<1),rs=(ls|1);
if(tot<=mid){mdf(Le,mid,ls);return 0;}
else {
R op=mdf(mid+1,Ri,rs);
if(op)upd(i);return op;
}
}
void query(R Le,R Ri,R l,R r,R i){//K
if(Le==l&&Ri==r){
R le=0,ri=G[i].size()-1,res=-1;
while(le<=ri){
R Mid=(le+ri)>>1;
if(K>G[i][Mid].r)le=Mid+1;
else res=Mid,ri=Mid-1;
}
ans=(1ll*ans*G[i][res].a%m+G[i][res].b)%m;
return ;
}
R mid=(Le+Ri)>>1,ls=(i<<1),rs=(ls|1);
if(r<=mid)query(Le,mid,l,r,ls);
else if(l>mid)query(mid+1,Ri,l,r,rs);
else query(Le,mid,l,mid,ls),query(mid+1,Ri,mid+1,r,rs);
}
int main(){
S=gi(),n=gi(),m=gi();
for(R i=1;i<=n;++i)w[i]=gi();
q=gi();
for(R t=1;t<=q;++t){
op=gi();
if(op==1){
l=gi(),r=gi(),a=gi()%m,b=gi()%m,++tot;
if(S&1)l^=ans,r^=ans;
mdf(1,Mx,1);//l r a b;
}
else {
l=gi(),r=gi(),K=gi();if(S&1)l^=ans,r^=ans,K^=ans;
ans=w[K],query(1,Mx,l,r,1);
printf("%d\n",ans);
}
}
return 0;
}