清華集訓2014 玄學
阿新 • • 發佈:2018-12-09
- 清華集訓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;
}