【模板整合计划】高阶数据结构
【模板整合计划】高阶数据结构
一:【并查集】
1.【路径压缩】
#include<cstdio>
#define Re register int
const int N=1e4+3;
int n,x,y,T,op,fa[N];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
inline int find(Re x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int main(){
in(n),in(T);
for(Re i=1;i<=n;++i)fa[i]=i;
while(T--){
in(op),in(x),in(y);
if(op<2){if(find(x)!=find(y))fa[find(x)]=find(y);}
else puts(find(x)==find(y)?"Y":"N");
}
}
2.【按秩合并(启发式合并)】
(1).【Deep】
#include<cstdio>
#define Re register int
const int N=1e4+3;
int n,x,y,T,op,fa[N],deep[N];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
inline int find(Re x){return x==fa[x]?x:find(fa[x]);}
inline void merge(Re x,Re y){
x=find(x),y=find(y);
if(x==y)return;
if(deep[x]==deep[y])++deep[fa[x]=y];
else if(deep[x]>deep[y])fa[y]=x;
else fa[x]=y;
}
int main(){
in(n),in(T);
for(Re i=1;i<=n;++i)fa[i]=i;
while(T--){
in(op),in(x),in(y);
if(op<2)merge(x,y);
else puts(find(x)==find(y)?"Y":"N");
}
}
(2).【Size】
#include<algorithm>
#include<cstdio>
#define Re register int
const int N=1e4+3;
int n,x,y,T,op,fa[N],size[N],deep[N];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
inline int find(Re x){return x==fa[x]?x:find(fa[x]);}
inline void merge(Re x,Re y){
x=find(x),y=find(y);
if(x==y)return;
if(size[x]>size[y])std::swap(x,y);
fa[x]=y,size[y]+=size[x];
}
int main(){
in(n),in(T);
for(Re i=1;i<=n;++i)fa[i]=i,size[i]=1;
while(T--){
in(op),in(x),in(y);
if(op<2)merge(x,y);
else puts(find(x)==find(y)?"Y":"N");
}
}
3.【边带权】
#include<algorithm>
#include<cstdio>
#define Re register int
using namespace std;
const int N=3e4+3;
int n,x,y,T,op,fa[N],dis[N],size[N];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
inline int find(Re x){
if(x==fa[x])return x;
Re rt=find(fa[x]);
dis[x]+=dis[fa[x]];
return fa[x]=rt;
}
inline void merge(Re x,Re y){
x=find(x),y=find(y);
fa[x]=y,dis[x]=size[y];
size[y]+=size[x];
}
int main(){
in(T);
for(Re i=1;i<=30000;++i)fa[i]=i,size[i]=1;
while(T--){
scanf(" %c",&op),in(x),in(y);
if(op=='M')merge(x,y);
else printf("%d\n",find(x)!=find(y)?-1:abs(dis[x]-dis[y])-1);
}
}
4.【扩展域】
#include<cstdio>
#define Re register int
const int N=5e4+3;
int n,a,b,c,T,b1,b2,b3,c1,c2,c3,ans,fa[N*3];
inline void in(Re &x){
Re fu=0;x=0;char ch=getchar();
while(ch<'0'||ch>'9')fu|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
x=fu?-x:x;
}
inline int find(Re x){return x==fa[x]?x:fa[x]=find(fa[x]);}
// fa[x],b1,c1 食物
// fa[x+n],b2,c2 天敌
// fa[x+2*n],b3,c3 同类
int main(){
in(n),in(T);
for(Re i=1;i<=n;++i)fa[i]=i,fa[i+n]=i+n,fa[i+2*n]=i+2*n;
while(T--){
in(a),in(b),in(c);
if(b>n||c>n){++ans;continue;}
if(a>1){//b吃c
if(b==c){++ans;continue;};
//食物 天敌 同类
b1=find(b),b2=find(b+n),b3=find(b+2*n);
c1=find(c),c2=find(c+n),c3=find(c+2*n);
if(b2==c3||b3==c1||b3==c3){++ans;continue;};
fa[b1]=fa[c3],fa[c2]=fa[b3],fa[b2]=fa[c1];
}
else{//b,c是同类
//食物 天敌 同类
b1=find(b),b2=find(b+n),b3=find(b+2*n);
c1=find(c),c2=find(c+n),c3=find(c+2*n);
if(b1==c3||b2==c3||c1==b3||c2==b3){++ans;continue;};
fa[b3]=fa[c3],fa[b1]=fa[c1],fa[b2]=fa[c2];
}
}
printf("%d",ans);
}
二:【树状数组】
三:【线段树】
四:【分块】
五:【可并堆】
1.【左偏树】
【模板】 左偏树(可并堆)\(\text{[P3377]}\)
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define Re register int
using namespace std;
const int N=1e5+5;
int n,x,y,T,op,A[N];
inline void in(Re &x){
Re fu=0;x=0;char ch=getchar();
while(ch<'0'||ch>'9')fu|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
x=fu?-x:x;
}
struct Leftist_Tree{
int fa[N],pl[N],pr[N],dis[N];
inline int merge(Re x,Re y){
if(!x||!y)return x+y;
if(A[x]>A[y]||(A[x]==A[y]&&x>y))swap(x,y);
pr[x]=merge(pr[x],y);
if(dis[pl[x]]<dis[pr[x]])swap(pl[x],pr[x]);
dis[x]=dis[pr[x]]+1;
if(pl[x])fa[pl[x]]=x;
if(pr[x])fa[pr[x]]=x;
return x;
}
inline int top(Re x){return fa[x]==x?x:fa[x]=top(fa[x]);}
inline void pop(Re x){
A[x]=-1,fa[pl[x]]=pl[x],fa[pr[x]]=pr[x];
fa[x]=merge(pl[x],pr[x]),pl[x]=pr[x]=0;
}
}T1;
int main(){
// freopen("123.txt","r",stdin);
in(n),in(T);
for(Re i=1;i<=n;++i)in(A[i]),T1.fa[i]=i;
while(T--){
in(op),in(x);
if(op&1){
in(y);
if(A[x]!=-1&&A[y]!=-1){
x=T1.top(x),y=T1.top(y);
if(x!=y)T1.merge(x,y);
}
}
else if(A[x]==-1)puts("-1");
else printf("%d\n",A[T1.top(x)]),T1.pop(T1.top(x));
}
}
五:【CDQ 分治】
【模板】 三维偏序(陌上花开)\(\text{[P3810]}\)
#include<algorithm>
#include<cstdio>
#define Re register int
using std::sort;
const int N=1e5+5,K=2e5+5;
int n,m,k,C[K],ans[N];
struct QAQ{int a,b,c,cnt,ans;}a[N],Q[N];//cnt为副本,ans为严格小于它的点的数量
inline void in(Re &x){
int fu=0;x=0;char c=getchar();
while(c<'0'||c>'9')fu|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=fu?-x:x;
}
inline bool cmp1(QAQ p,QAQ o){return p.a!=o.a?p.a<o.a:(p.b!=o.b?p.b<o.b:p.c<o.c);}
inline bool cmp2(QAQ p,QAQ o){return p.b<o.b;}
inline void add(Re x,Re v){while(x<=k)C[x]+=v,x+=x&-x;}
inline int ask(Re x){
Re ans=0;
while(x)ans+=C[x],x-=x&-x;
return ans;
}
inline void CDQ(Re L,Re R){
if(L==R)return;
Re mid=L+R>>1;
CDQ(L,mid),CDQ(mid+1,R);
sort(Q+L,Q+mid+1,cmp2),sort(Q+mid+1,Q+R+1,cmp2);
Re j=L;
for(Re i=mid+1;i<=R;++i){
while(j<=mid&&Q[j].b<=Q[i].b)add(Q[j].c,Q[j].cnt),++j;
Q[i].ans+=ask(Q[i].c);
}
for(Re i=L;i<j;++i)add(Q[i].c,-Q[i].cnt);//这里是小于j而不是mid
}
int main(){
in(n),in(k);
for(Re i=1;i<=n;++i)in(a[i].a),in(a[i].b),in(a[i].c);
sort(a+1,a+n+1,cmp1);
for(Re i=1;i<=n;++i){
if(i>2&&a[i].a==a[i-1].a&&a[i].b==a[i-1].b&&a[i].c==a[i-1].c)++Q[m].cnt;
else Q[++m]=a[i],Q[m].cnt=1;
}
CDQ(1,m);
for(Re i=1;i<=m;++i)ans[Q[i].ans+Q[i].cnt-1]+=Q[i].cnt;
for(Re i=0;i<n;++i)printf("%d\n",ans[i]);
}
六:【整体二分】
1.【整体二分】
【模板】 \(\text{K}\) 大数查询\(\text{[ZJOI2013] [P3332]}\)
#include<algorithm>
#include<cstring>
#include<cstdio>
#define Re register int
#define LL long long
using namespace std;
const int N=5e4+3,M=5e4+3,inf=1e9;
int n,x,T,Ans[M];
struct QAQ{int l,r,op,id;LL k;}Q[N+M],Q1[N+M],Q2[N+M];
inline void in(Re &x){
int fu=0;x=0;char c=getchar();
while(c<'0'||c>'9')fu|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=fu?-x:x;
}
struct Segment_Tree{
#define pl p<<1
#define pr p<<1|1
#define Mid (L+R>>1)
struct QAQ{int CL,add;LL S;}tr[N<<2];
inline void pushup(Re p){tr[p].S=tr[pl].S+tr[pr].S;}
inline void updata(Re p,Re L,Re R,Re v){tr[p].S+=v*(R-L+1),tr[p].add+=v;}
inline void updata_(Re p){tr[p].add=tr[p].S=0,tr[p].CL=1;}
inline void pushdown(Re p,Re L,Re R){
if(tr[p].CL)updata_(pl),updata_(pr),tr[p].CL=0;
if(tr[p].add)updata(pl,L,Mid,tr[p].add),updata(pr,Mid+1,R,tr[p].add),tr[p].add=0;
}
inline void change(Re p,Re L,Re R,Re l,Re r,Re v){
if(l<=L&&R<=r){updata(p,L,R,v);return;}
pushdown(p,L,R);
if(l<=Mid)change(pl,L,Mid,l,r,v);
if(r>Mid)change(pr,Mid+1,R,l,r,v);
pushup(p);
}
inline LL ask(Re p,Re L,Re R,Re l,Re r){
if(l<=L&&R<=r)return tr[p].S;
LL ans=0;pushdown(p,L,R);
if(l<=Mid)ans+=ask(pl,L,Mid,l,r);
if(r>Mid)ans+=ask(pr,Mid+1,R,l,r);
return ans;
}
}T1;
inline void sakura(Re l,Re r,Re L,Re R){
if(L>R)return;
if(l==r){
for(Re i=L;i<=R;++i)if(Q[i].op>1)Ans[Q[i].id]=l;
return;
}
Re mid=l+r>>1,m1=0,m2=0;
for(Re i=L;i<=R;++i)
if(Q[i].op&1){
if(Q[i].k<=mid)Q1[++m1]=Q[i];
else Q2[++m2]=Q[i],T1.change(1,1,n,Q[i].l,Q[i].r,1);
}
else{
LL tmp=T1.ask(1,1,n,Q[i].l,Q[i].r);
if(Q[i].k<=tmp)Q2[++m2]=Q[i];
else Q[i].k-=tmp,Q1[++m1]=Q[i];
}
T1.updata_(1);
for(Re i=1;i<=m1;++i)Q[L+i-1]=Q1[i];
for(Re i=1;i<=m2;++i)Q[L+m1+i-1]=Q2[i];
sakura(l,mid,L,L+m1-1);
sakura(mid+1,r,L+m1,R);
}
int main(){
// freopen("123.txt","r",stdin);
in(n),in(T);
for(Re i=1;i<=T;++i){
in(Q[i].op),in(Q[i].l),in(Q[i].r),in(x),Q[i].k=x;
if(Q[i].op>1)Q[i].id=++Ans[0];
}
sakura(-n,n,1,T);
for(Re i=1;i<=Ans[0];++i)printf("%d\n",Ans[i]);
}
2.【静态区间第 K 小】
【模板】 可持久化线段树 \(1\)(主席树)\(\text{[P3834]}\)
#include<algorithm>
#include<cstring>
#include<cstdio>
#define Re register int
#define LL long long
using namespace std;
const int N=2e5+3,M=2e5+3,inf=1e9;
int n,x,y,z,T,cnt,Ans[M];char op;
struct QAQ{int l,r,k,op,id;}Q[N+M],Q1[N+M],Q2[N+M];
inline void in(Re &x){
int fu=0;x=0;char c=getchar();
while(c<'0'||c>'9')fu|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=fu?-x:x;
}
struct BIT{
int C[N];
inline void add(Re x,Re v){while(x<=n)C[x]+=v,x+=x&-x;}
inline int ask_(Re x){Re ans=0;while(x)ans+=C[x],x-=x&-x;return ans;}
inline int ask(Re l,Re r){return ask_(r)-ask_(l-1);}
}T1;
inline void sakura(Re l,Re r,Re L,Re R){
if(L>R)return;
if(l==r){
for(Re i=L;i<=R;++i)if(Q[i].op>1)Ans[Q[i].id]=l;
return;
}
Re mid=l+r>>1,m1=0,m2=0;
for(Re i=L;i<=R;++i)
if(Q[i].op&1){
if(Q[i].k<=mid)Q1[++m1]=Q[i],T1.add(Q[i].id,1);
else Q2[++m2]=Q[i];
}
else{
Re tmp=T1.ask(Q[i].l,Q[i].r);
if(Q[i].k<=tmp)Q1[++m1]=Q[i];
else Q[i].k-=tmp,Q2[++m2]=Q[i];
}
for(Re i=1;i<=m1;++i)if(Q1[i].op&1)T1.add(Q1[i].id,-1);
for(Re i=1;i<=m1;++i)Q[L+i-1]=Q1[i];
for(Re i=1;i<=m2;++i)Q[L+m1+i-1]=Q2[i];
sakura(l,mid,L,L+m1-1);
sakura(mid+1,r,L+m1,R);
}
int main(){
freopen("123.txt","r",stdin);
in(n),in(T);
for(Re i=1;i<=n;++i)in(x),Q[++cnt]=(QAQ){0,0,x,1,i};
for(Re i=1;i<=T;++i)in(x),in(y),in(z),Q[++cnt]=(QAQ){x,y,z,2,i};
sakura(-inf,inf,1,cnt);
for(Re i=1;i<=T;++i)printf("%d\n",Ans[i]);
}
3.【动态区间第 K 小】
【模板】 \(\text{Dynamic Rankings [P3834]}\)
#include<algorithm>
#include<cstring>
#include<cstdio>
#define Re register int
#define LL long long
using namespace std;
const int N=1e5+3,M=1e5+3,inf=1e9;
int n,x,y,z,T,cnt,a[N],Ans[M];char op;
struct QAQ{int l,r,k,op,id;}Q[N+M*2],Q1[N+M*2],Q2[N+M*2];
inline void in(Re &x){
int fu=0;x=0;char c=getchar();
while(c<'0'||c>'9')fu|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=fu?-x:x;
}
struct BIT{
int C[N];
inline void add(Re x,Re v){while(x<=n)C[x]+=v,x+=x&-x;}
inline int ask_(Re x){Re ans=0;while(x)ans+=C[x],x-=x&-x;return ans;}
inline int ask(Re l,Re r){return ask_(r)-ask_(l-1);}
}T1;
inline void sakura(Re l,Re r,Re L,Re R){
if(L>R)return;
if(l==r){
for(Re i=L;i<=R;++i)if(Q[i].op>1)Ans[Q[i].id]=l;
return;
}
Re mid=l+r>>1,m1=0,m2=0;
for(Re i=L;i<=R;++i)
if(Q[i].op&1){
if(Q[i].l<=mid)Q1[++m1]=Q[i],T1.add(Q[i].id,Q[i].r);
else Q2[++m2]=Q[i];
}
else{
Re tmp=T1.ask(Q[i].l,Q[i].r);
if(Q[i].k<=tmp)Q1[++m1]=Q[i];
else Q[i].k-=tmp,Q2[++m2]=Q[i];
}
for(Re i=1;i<=m1;++i)if(Q1[i].op&1)T1.add(Q1[i].id,-Q1[i].r);
for(Re i=1;i<=m1;++i)Q[L+i-1]=Q1[i];
for(Re i=1;i<=m2;++i)Q[L+m1+i-1]=Q2[i];
sakura(l,mid,L,L+m1-1);
sakura(mid+1,r,L+m1,R);
}
int main(){
freopen("123.txt","r",stdin);
in(n),in(T);
for(Re i=1;i<=n;++i)in(a[i]),Q[++cnt]=(QAQ){a[i],1,0,1,i};
while(T--){
scanf(" %c",&op),in(x),in(y);
if(op=='C')Q[++cnt]=(QAQ){a[x],-1,0,1,x},Q[++cnt]=(QAQ){a[x]=y,1,0,1,x};
else in(z),Q[++cnt]=(QAQ){x,y,z,2,++Ans[0]};
}
sakura(-inf,inf,1,cnt);
for(Re i=1;i<=Ans[0];++i)printf("%d\n",Ans[i]);
}
七:【莫队】
1.【静态莫队】
【模板】 \(\text{D-query}\) \(\text{[SP3267]}\)
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define Re register int
using namespace std;
const int N=3e4+3,M=2e5+3;
int n,T,BL,ans,A[N],cnt[1000003],Ans[M];
struct Query{
int l,r,id;
inline bool operator<(const Query &O)const{
Re B1=l/BL,B2=O.l/BL;
return B1!=B2?B1<B2:((B1&1)?r<O.r:r>O.r);
}
}Q[M];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
inline void add(Re x){ans+=!cnt[x]++;}
inline void del(Re x){ans-=!--cnt[x];}
int main(){
in(n),BL=sqrt(n)+1;
for(Re i=1;i<=n;++i)in(A[i]);
in(T);
for(Re i=1;i<=T;++i)in(Q[i].l),in(Q[i].r),Q[i].id=i;
sort(Q+1,Q+T+1);Re nowL=1,nowR=0;
for(Re i=1;i<=T;++i){
while(nowR<Q[i].r)add(A[++nowR]);
while(nowR>Q[i].r)del(A[nowR--]);
while(nowL>Q[i].l)add(A[--nowL]);
while(nowL<Q[i].l)del(A[nowL++]);
Ans[Q[i].id]=ans;
}
for(Re i=1;i<=T;++i)printf("%d\n",Ans[i]);
}
2.【带修莫队】
【模板】 数颜色 / 维护队列 \(\text{[P1903]}\)
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#define Re register int
using namespace std;
const int N=2e5+3,M=2e5+3,INF=1e6+3;
int n,x,y,T,TQ,TC,BL,ans,a[N],Ans[M],cnt[INF];char op;
struct Query{
int l,r,id,time;
inline bool operator<(const Query &O)const{
Re B1=l/BL,B2=O.l/BL,B3=r/BL,B4=O.r/BL;
return B1!=B2?B1<B2:(B3!=B4?B3<B4:time<O.time);
}
}Q[N];
struct Change{int w,co;}C[N];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
inline void add(Re x){ans+=!cnt[x]++;}
inline void del(Re x){ans-=!--cnt[x];}
int main(){
in(n),in(T),BL=pow(n,2.0/3.0);
for(Re i=1;i<=n;++i)in(a[i]);
for(Re i=1;i<=T;++i){
scanf(" %c",&op);
in(x),in(y);
if(op=='Q')++TQ,Q[TQ]=(Query){x,y,TQ,TC};
else C[++TC]=(Change){x,y};
}
sort(Q+1,Q+TQ+1);
Re nowL=1,nowR=0,nowT=0;
for(Re i=1;i<=TQ;++i){
while(nowL<Q[i].l)del(a[nowL++]);
while(nowL>Q[i].l)add(a[--nowL]);
while(nowR>Q[i].r)del(a[nowR--]);
while(nowR<Q[i].r)add(a[++nowR]);
while(nowT<Q[i].time){
Re w=C[++nowT].w;
if(nowL<=w&&w<=nowR)del(a[w]),add(C[nowT].co);
swap(a[w],C[nowT].co);
}
while(nowT>Q[i].time){
Re w=C[nowT].w;
if(nowL<=w&&w<=nowR)del(a[w]),add(C[nowT].co);
swap(a[w],C[nowT--].co);
}
Ans[Q[i].id]=ans;
}
for(Re i=1;i<=TQ;++i)printf("%d\n",Ans[i]);
}
3.【树上静态莫队】
【模板】 \(\text{COT2 - Count on a tree II [SP10707]}\)
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define Re register int
using namespace std;
const int N=4e4+3,M=1e5+3,INF=1e6+3,logN=16;
int n,m,o,x,y,T,BL,id_O,b[N],A[N],B[N<<1],head[N],last[N],first[N];
struct QAQ{int to,next;}a[N<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline void in(Re &x){
Re fu=0;x=0;char c=getchar();
while(c<'0'||c>'9')fu|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=fu?-x:x;
}
struct LCA{
int deep[N],ant[N][20];
inline void dfs(Re x,Re fa){
first[B[++B[0]]=x]=++id_O;
deep[x]=deep[ant[x][0]=fa]+1;
for(Re i=1;(1<<i)<=deep[x];++i)ant[x][i]=ant[ant[x][i-1]][i-1];
for(Re i=head[x];i;i=a[i].next)if(a[i].to!=fa)dfs(a[i].to,x);
last[B[++B[0]]=x]=++id_O;
}
inline int lca(Re x,Re y){
if(deep[x]<deep[y])swap(x,y);
for(Re i=logN;i>=0;--i)if(deep[ant[x][i]]>=deep[y])x=ant[x][i];
if(x==y)return x;
for(Re i=logN;i>=0;--i)if(ant[x][i]!=ant[y][i])x=ant[x][i],y=ant[y][i];
return ant[x][0];
}
}T1;
struct Query{
int l,r,id,lca;
inline bool operator<(const Query &O)const{
Re B1=l/BL,B2=O.l/BL;
return B1!=B2?B1<B2:((B1&1)?r<O.r:r>O.r);
}
}Q[M];
int ans,vis[N<<1],Ans[M],cnt[INF];
inline void change(Re x){
vis[x]?ans-=!--cnt[A[x]]:ans+=!cnt[A[x]]++,vis[x]^=1;
}
int main(){
// freopen("123.txt","r",stdin);
in(n),in(T),BL=sqrt(n<<1);
for(Re i=1;i<=n;++i)in(A[i]),b[i]=A[i];
sort(b+1,b+n+1),m=unique(b+1,b+n+1)-b-1;
for(Re i=1;i<=n;++i)A[i]=lower_bound(b+1,b+m+1,A[i])-b;
m=n-1;while(m--)in(x),in(y),add(x,y),add(y,x);
T1.dfs(1,0);
for(Re i=1;i<=T;++i){
in(x),in(y);Re lca=T1.lca(x,y);
if(first[x]>first[y])swap(x,y);
if(lca==x)Q[i]=(Query){first[x],first[y],i,0};
else Q[i]=(Query){last[x],first[y],i,lca};
}
sort(Q+1,Q+T+1);
Re nowL=1,nowR=0;
for(Re i=1;i<=T;++i){
Re L=Q[i].l,R=Q[i].r,lca=Q[i].lca;
while(nowL<L)change(B[nowL++]);
while(nowL>L)change(B[--nowL]);
while(nowR>R)change(B[nowR--]);
while(nowR<R)change(B[++nowR]);
if(lca)change(lca);
Ans[Q[i].id]=ans;
if(lca)change(lca);
}
for(Re i=1;i<=T;++i)printf("%d\n",Ans[i]);
}
4.【树上带修莫队】
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define Re register int
using namespace std;
const int N=1e5+3,M=1e5+3,logN=17;
int n,m,x,y,o,T,op,id_O,A[N],V[N],W[N],B[N],head[N],last[N],first[N];
struct QAQ{int to,next;}a[N<<1];
inline void add(Re x,Re y){a[++o].to=y,a[o].next=head[x],head[x]=o;}
inline void in(Re &x){
Re fu=0;x=0;char c=getchar();
while(c<'0'||c>'9')fu|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=fu?-x:x;
}
struct LCA{
int deep[N],ant[N][20];
inline void dfs(Re x,Re fa){
first[B[++B[0]]=x]=++id_O;
deep[x]=deep[ant[x][0]=fa]+1;
for(Re i=1;(1<<i)<=deep[x];++i)ant[x][i]=ant[ant[x][i-1]][i-1];
for(Re i=head[x];i;i=a[i].next)if(a[i].to!=fa)dfs(a[i].to,x);
last[B[++B[0]]=x]=++id_O;
}
inline int lca(Re x,Re y){
if(deep[x]<deep[y])swap(x,y);
for(Re i=logN;i>=0;--i)if(deep[ant[x][i]]>=deep[y])x=ant[x][i];
if(x==y)return x;
for(Re i=logN;i>=0;--i)if(ant[x][i]!=ant[y][i])x=ant[x][i],y=ant[y][i];
return ant[x][0];
}
}T1;
int BL,TQ,TC,vis[N],cnt[N];LL ans,Ans[M];
struct Query{
int l,r,id,lca,time;
inline bool operator<(const Query &O)const{
Re B1=l/BL,B2=O.l/BL,B3=r/BL,B4=O.r/BL;
return B1!=B2?B1<B2:(B3!=B4?B3<B4:time<O.time);
}
}Q[M];
struct Change{int w,co;}C[M];
inline void change(Re x){
vis[x]?ans-=(LL)V[A[x]]*W[cnt[A[x]]--]:ans+=(LL)V[A[x]]*W[++cnt[A[x]]],vis[x]^=1;
}
int main(){
freopen("123.txt","r",stdin);
in(n),in(m),in(T),BL=pow(n<<1,2.0/3.0);
for(Re i=1;i<=m;++i)in(V[i]);
for(Re i=1;i<=n;++i)in(W[i]);
for(Re i=1;i<n;++i)in(x),in(y),add(x,y),add(y,x);
for(Re i=1;i<=n;++i)in(A[i]);
T1.dfs(1,0);
for(Re i=1;i<=T;++i){
in(op),in(x),in(y);
if(op){
Re lca=T1.lca(x,y);
if(first[x]>first[y])swap(x,y);
if(lca==x)++TQ,Q[TQ]=(Query){first[x],first[y],TQ,0,TC};
else ++TQ,Q[TQ]=(Query){last[x],first[y],TQ,lca,TC};
}
else C[++TC]=(Change){x,y};
}
sort(Q+1,Q+TQ+1);
Re nowL=1,nowR=0,nowT=0;
for(Re i=1;i<=TQ;++i){
Re L=Q[i].l,R=Q[i].r,T=Q[i].time,lca=Q[i].lca;
while(nowL<L)change(B[nowL++]);
while(nowL>L)change(B[--nowL]);
while(nowR>R)change(B[nowR--]);
while(nowR<R)change(B[++nowR]);
while(nowT<T){
Re w=C[++nowT].w;
if(vis[w])change(w),swap(A[w],C[nowT].co),change(w);
else swap(A[w],C[nowT].co);
}
while(nowT>T){
Re w=C[nowT].w;
if(vis[w])change(w),swap(A[w],C[nowT].co),change(w);
else swap(A[w],C[nowT].co);
--nowT;
}
if(lca)change(lca);
Ans[Q[i].id]=ans;
if(lca)change(lca);
}
for(Re i=1;i<=TQ;++i)printf("%lld\n",Ans[i]);
}
5.【回滚莫队】
【模板】 歴史の研究 \(\text{[AT1219]}\)
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define Re register int
using namespace std;
const int N=1e5+3,M=1e5+3;
int n,m,T,BL,BLN,A[N],b[N],Li[325],Ri[325],cnt[N],cnt_[N],belong[N];LL Ans[M];
struct Query{
int l,r,id;
inline bool operator<(const Query &O)const{
Re B1=belong[l],B2=belong[O.l];
return B1!=B2?B1<B2:r<O.r;
}
}Q[M];
inline void in(Re &x){
Re fu=0;x=0;char c=getchar();
while(c<'0'||c>'9')fu|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=fu?-x:x;
}
int main(){
// freopen("123.txt","r",stdin);
in(n),in(T),BL=sqrt(n)+1,BLN=n/BL+1;
for(Re i=1;i<=BLN;++i){
Li[i]=(i-1)*BL+1,Ri[i]=min(i*BL,n);
for(Re j=Li[i];j<=Ri[i];++j)belong[j]=i;
}
for(Re i=1;i<=n;++i)in(A[i]),b[i]=A[i];
sort(b+1,b+n+1),m=unique(b+1,b+n+1)-b-1;
for(Re i=1;i<=n;++i)A[i]=lower_bound(b+1,b+m+1,A[i])-b;
for(Re i=1;i<=T;++i)in(Q[i].l),in(Q[i].r),Q[i].id=i;
sort(Q+1,Q+T+1);Re i=1;
for(Re p=1;p<=BLN;++p){
Re nowL=Ri[p]+1,nowR=Ri[p];LL ans=0;
memset(cnt,0,sizeof(cnt));
while(belong[Q[i].l]==p){
Re L=Q[i].l,R=Q[i].r;
if(belong[L]==belong[R]){
LL tmp=0;
for(Re j=L;j<=R;++j)cnt_[A[j]]=0;
for(Re j=L;j<=R;++j)++cnt_[A[j]],tmp=max(tmp,(LL)b[A[j]]*cnt_[A[j]]);
Ans[Q[i].id]=tmp;
}
else{
while(nowR<R)cnt[A[++nowR]]++,ans=max(ans,(LL)b[A[nowR]]*cnt[A[nowR]]);
LL tmp=ans;
while(nowL>L)cnt[A[--nowL]]++,tmp=max(tmp,(LL)b[A[nowL]]*cnt[A[nowL]]);
Ans[Q[i].id]=tmp;
while(nowL<Ri[p]+1)--cnt[A[nowL++]];
}
++i;
}
}
for(Re i=1;i<=T;++i)printf("%lld\n",Ans[i]);
}
八:【平衡树】
1.【Treap】
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<ctime>
#define Re register int
#define pl tr[p].lp
#define pr tr[p].rp
#define pv tr[p].v
using namespace std;
const int N=1e5+5,inf=2e9;
int a,b,o,T,root;
inline void in(Re &x){
int fu=0;x=0;char c=getchar();
while(c<'0'||c>'9')fu|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=fu?-x:x;
}
struct Treap{int v,rd,lp,rp,cnt,size;}tr[N];//cnt为该节点处这个数v的个数
inline void pushup(Re p){tr[p].size=tr[p].cnt+tr[pl].size+tr[pr].size;}//更新节点空间信息
inline void lturn(Re &p){//左旋
Re q=pr;pr=tr[q].lp,tr[q].lp=p;//颠倒父子关系
pushup(p),pushup(p=q);//先push(p)再push(q)并拉好最上面的线
//拉线:p=q。这里的p经过无数次取地址符反复调用而来,回溯至初相当于tr[?].lp或者tr[?].rp
}
inline void rturn(Re &p){//右旋
Re q=pl;pl=tr[q].rp,tr[q].rp=p;//颠倒父子关系
pushup(p),pushup(p=q);//先push(p)再push(q)并拉好最上面的线
}
inline void insert(Re &p,Re x){
if(!p){tr[p=++o].v=x,tr[p].cnt=tr[p].size=1,tr[p].rd=rand();return;}
++tr[p].size;
if(x==pv)++tr[p].cnt;
else if(x<pv){
insert(pl,x);
if(tr[pl].rd<tr[p].rd)rturn(p);//小跟堆
}
else{
insert(pr,x);
if(tr[pr].rd<tr[p].rd)lturn(p);//小跟堆
}
}
inline void del(Re &p,Re x){
if(!p)return;
if(x<pv)--tr[p].size,del(pl,x);
else if(x>pv)--tr[p].size,del(pr,x);
else{
if(tr[p].cnt>1)--tr[p].cnt,--tr[p].size;//如果这个数的个数不止一个,直接减一轻松退出
else{//否则需要删除该节点
if(pl*pr==0)p=pl+pr;
//其中一个儿子为空,直接指向这个不空的,那么访问时就可以直接跳过节点p,相当于达到了删除p的目的
//如果都为空,相当于把p赋值成了0
//否则的话,先调整好两个儿子的次序,然后将p丢到一边并继续删除操作
else if(tr[pl].rd<tr[pr].rd)rturn(p),del(p,x);
else lturn(p),del(p,x);
}
}
}
inline int ask_level(Re p,Re x){//查询数x的排名(保证存在x)。
// if(!p)return 0;//如果没找到这个数
if(x==pv)return tr[pl].size+1;//找到了这个数
else return x<pv?ask_level(pl,x):tr[p].cnt+tr[pl].size+ask_level(pr,x);
}
inline int ask_v(Re p,Re x){//查询排名为x的数
// if(!p)return 0;//如果没找到这个数
if(x<=tr[pl].size)return ask_v(pl,x);
else{
Re tmp=tr[pl].size+tr[p].cnt;
return x<=tmp?pv:ask_v(pr,x-tmp);
}
}
inline int ask_pri(Re p,Re x){//查询x的前驱
if(!p)return -inf;
if(x<=pv)return ask_pri(pl,x);
else return max(pv,ask_pri(pr,x));
}
inline int ask_nex(Re p,Re x){//查询x的后继
if(!p)return inf;
if(x>=pv)return ask_nex(pr,x);
else return min(pv,ask_nex(pl,x));
}
int main(){
srand((int)time(NULL));
in(T);
while(T--){
in(a),in(b);
if(a==1)insert(root,b);
if(a==2)del(root,b);
if(a==3)printf("%d\n",ask_level(root,b));
if(a==4)printf("%d\n",ask_v(root,b));
if(a==5)printf("%d\n",ask_pri(root,b));
if(a==6)printf("%d\n",ask_nex(root,b));
}
}
2.【FHQ Treap】
3.【伸展树 (Splay)】
(1).【维护堆】
#include<algorithm>
#include<cstdio>
#define Re register int
#define pl tr[p].ps[0]
#define pr tr[p].ps[1]
#define pf tr[p].fa
#define pv tr[p].v
using namespace std;
const int N=1e5+5,inf=2e9;
int T,a,b,n,o,root;
struct Splay{int v,fa,ps[2],cnt,size;}tr[N];
inline void in(Re &x){
int fu=0;x=0;char c=getchar();
while(c<'0'||c>'9')fu|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=fu?-x:x;
}
inline void pushup(Re p){if(!p)return;/*这里必需要加判断return,否则会崩死*/tr[p].size=tr[p].cnt+tr[pl].size+tr[pr].size;}
inline void connect(Re p,Re f,Re o){tr[tr[p].fa=f].ps[o]=p;}//让p成为f的o儿子
inline int which(Re p){return tr[pf].ps[1]==p;}//查询p是哪个儿子
inline void rotate(Re p){//Treap是把p往下拉,这里是把p往上拉到pf的头上去
Re y=pf,ys=which(p);//【y】x的父结点。【ys】x是它父亲的哪个儿子
Re R=tr[y].fa,Rs=which(y);//【R】y的父亲。【Rs】y是它父亲的哪个儿子
Re B=tr[p].ps[ys^1];//p的儿子中与pf同方向的点(需要将其连到y上)
connect(B,y,ys),connect(y,p,ys^1),connect(p,R,Rs);//改线
pushup(y),pushup(p);//p被拉到了y头上去,先02更新y,再更新p
}
inline void Splay(Re p){//无论如何都一定要一次旋两下,NOI的巨毒瘤数据会卡这里
for(Re fa;fa=pf;rotate(p))
if(tr[fa].fa)rotate(which(p)==which(fa)?fa:p);
root=p;
//线性卡单旋,人字形卡双旋
}
inline void New(Re x,Re fa){//建一个新点x并接到 fa下面
tr[++o].v=x,tr[o].fa=fa,tr[o].cnt=tr[o].size=1;
if(fa)tr[fa].ps[tr[fa].v<x]=o;
}
inline void insert(Re x){
if(!root){New(x,0);root=o;return;}//空树则直接简单接入
int p=root,fa=0;
while(1){
if(x==pv){++tr[p].cnt,pushup(p),pushup(fa),Splay(p);break;}//发现了副本
fa=p,p=tr[p].ps[pv<x];//继续往下遍历
if(!p){New(x,fa),pushup(fa),Splay(o);break;}//到达叶子节点位置,插入并将其拉到顶上去
}
}
inline int ask_level(Re p,Re x){//查询数x的排名
//printf("p: %d\n",p);exit(0);
if(x==pv){Re ans=tr[pl].size+1;Splay(p);return ans;}//找到了这个数
else return x<pv?ask_level(pl,x):tr[p].cnt+tr[pl].size+ask_level(pr,x);
}
inline int ask_v(Re p,Re x){//查询排名为x的数
// if(!p)return 0;//如果没找到这个数
if(x<=tr[pl].size)return ask_v(pl,x);
else{
Re tmp=tr[pl].size+tr[p].cnt;
return x<=tmp?pv:ask_v(pr,x-tmp);
}
}
inline int ask_nex_p(Re p){p=pr;while(pl)p=pl;return p;}//从节点p开始查询猴急的位置
inline int ask_pri_p(Re p){p=pl;while(pr)p=pr;return p;}//从节点p开始查询前驱的位置
inline void del(Re x){
ask_level(root,x);Re p=root;//找到这个点并拉成root
if(tr[p].cnt>1){--tr[p].cnt,--tr[p].size;return;}//有副本
if(!pl&&!pr){root=0;return;}//没有儿子
if(!pl||!pr){tr[root=pl+pr].fa=0;return;}//只有一个儿子,直接将其儿子改为根
Splay(ask_pri_p(root));//把它的前驱拉上去,然后它的右儿子就是原root(即x)
tr[root].ps[1]=pr,tr[pr].fa=root;//删除原root(即x)
pushup(root);
}
int main(){
in(T);
while(T--){
in(a),in(b);
if(a==1)insert(b);
if(a==2)del(b);
if(a==3)printf("%d\n",ask_level(root,b));
if(a==4)printf("%d\n",ask_v(root,b));
if(a==5)insert(b),printf("%d\n",tr[ask_pri_p(root)].v),del(b);
if(a==6)insert(b),printf("%d\n",tr[ask_nex_p(root)].v),del(b);
}
}
(2).【维护序列】
【模板】 维护数列 \(\text{[NOI2005] [P2042]}\)
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
#define LL long long
#define Re register int
using namespace std;
const int N=2e5+3,inf=1e9;
int n,x,y,z,T,A[N];char op[20];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
struct Splay{
#define pl (tr[p].ps[0])
#define pr (tr[p].ps[1])
#define pf (tr[p].fa)
#define pv (tr[p].v)
int O,root,Q[N];queue<int>D;
struct QAQ{int v,S,fa,max,lmax,rmax,size,ps[2];bool tag,turn;}tr[N];
inline void pushup(Re p){
tr[p].S=tr[pl].S+tr[pr].S+pv;
tr[p].size=tr[pl].size+tr[pr].size+1;
tr[p].lmax=max(tr[pl].lmax,tr[pl].S+pv+tr[pr].lmax);
tr[p].rmax=max(tr[pr].rmax,tr[pr].S+pv+tr[pl].rmax);
tr[p].max=max(tr[pl].rmax+pv+tr[pr].lmax,max(tr[pl].max,tr[pr].max));
}
inline void updata(Re p){tr[p].turn^=1,swap(pl,pr),swap(tr[p].lmax,tr[p].rmax);}
inline void updata(Re p,Re v){
tr[p].tag=1,tr[p].S=(pv=v)*tr[p].size;
if(v>0)tr[p].lmax=tr[p].rmax=tr[p].max=tr[p].S;
else tr[p].lmax=tr[p].rmax=0,tr[p].max=v;
}
inline void pushdown(Re p){
if(tr[p].tag){
if(pl)updata(pl,pv);
if(pr)updata(pr,pv);
tr[p].tag=tr[p].turn=0;
}
if(tr[p].turn){
if(pl)updata(pl);
if(pr)updata(pr);
tr[p].turn=0;
}
}
inline int which(Re p){return tr[pf].ps[1]==p;}
inline void connect(Re p,Re fa,Re o){tr[pf=fa].ps[o]=p;}
inline void rotate(Re p){
Re fa=pf,fas=which(p);
Re pa=tr[fa].fa,pas=which(fa);
Re x=tr[p].ps[fas^1];
connect(x,fa,fas),connect(fa,p,fas^1),connect(p,pa,pas);
pushup(fa),pushup(p);
}
inline void splay(Re p,Re to){
Re x=p,t=0;Q[++t]=x;
while(tr[x].fa)Q[++t]=x=tr[x].fa;
while(t)pushdown(Q[t--]);
for(Re fa;pf!=to;rotate(p))
if(tr[fa=pf].fa!=to)rotate(which(p)==which(fa)?fa:p);
if(!to)root=p;
}
inline void CL(Re p){
tr[p].size=tr[p].turn=tr[p].tag=tr[p].lmax=tr[p].rmax=tr[p].S=pv=pf=pl=pr=0,tr[p].max=-inf;
}
inline int New(){
Re p;
if(D.empty())p=++O;
else p=D.front(),D.pop();
CL(p),tr[p].size=1;return p;
}
inline void build(Re &p,Re L,Re R,Re fa){
if(L>R)return;Re mid=(L+R)>>1;p=New(),pv=A[mid],pf=fa;
build(pl,L,mid-1,p),build(pr,mid+1,R,p);
pushup(p);
}
inline void recycle(Re p){
if(pl)recycle(pl);
if(pr)recycle(pr);
D.push(p),CL(p);
}
inline int find(Re p,Re K){
pushdown(p);
if(K<=tr[pl].size)return find(pl,K);
return K<=tr[pl].size+1?p:find(pr,K-tr[pl].size-1);
}
inline int split(Re L,Re R){
Re p=find(root,L-1),q=find(root,R+1);
splay(p,0),splay(q,p);return tr[q].ps[0];
}
inline void insert(Re st,Re m){
for(Re i=1;i<=m;++i)in(A[i]);
Re rt=0;build(rt,1,m,0);
Re p=find(root,st),q=find(root,st+1);
splay(p,0),splay(q,p);
connect(rt,q,0),pushup(q),pushup(p);
}
inline void erase(Re L,Re R){
Re p=split(L,R),fa=pf;
tr[fa].ps[0]=pf=0,recycle(p);
pushup(fa),pushup(tr[fa].fa);
}
inline int ask(Re L,Re R){return tr[split(L,R)].S;}
inline int ask_max(Re L,Re R){return tr[split(L,R)].max;}
inline void change(Re L,Re R,Re v){
Re p=split(L,R);updata(p,v),pushup(pf),pushup(tr[pf].fa);
}
inline void reverse(Re L,Re R){
Re p=split(L,R);
if(!tr[p].tag)updata(p),pushup(pf),pushup(tr[pf].fa);
}
}TR;
int main(){
in(n),in(T),TR.tr[0].max=A[1]=A[n+2]=-inf;
for(Re i=2;i<=n+1;++i)in(A[i]);
TR.build(TR.root,1,n+2,0);
while(T--){
scanf("%s",op);
if(op[0]=='M'&&op[2]=='X')in(x),in(y),++x,printf("%d\n",TR.ask_max(x,x+y-1));
else if(op[0]=='G'&&(int)strlen(op)==3)in(x),++x,printf("%d\n",TR.ask(x,x));
else{
in(x),in(y),++x;
if(op[0]=='I')TR.insert(x,y);
if(op[0]=='D')TR.erase(x,x+y-1);
if(op[0]=='M')in(z),TR.change(x,x+y-1,z);
if(op[0]=='R')TR.reverse(x,x+y-1);
if(op[0]=='G')printf("%d\n",TR.ask(x,x+y-1));
}
}
}
- 【模板】 文艺平衡树 \(\text{[P3391]}\)(维护序列简化版)
4.【AVL】
5.【SBT】
6.【红黑树】
7.【替罪羊树】
九:【珂朵莉树】
【模板】 \(\text{Willem, Chtholly and Seniorious}\) \(\text{CF896C}\)
#include<algorithm>
#include<cstdio>
#include<vector>
#include<set>
#define LL long long
#define Re register int
#define IT set<QAQ>::iterator
using namespace std;
const int N=1e5+5;
struct QAQ{
int l,r;mutable LL v;
QAQ(int L,int R=-1,LL V=0):l(L),r(R),v(V){}
bool operator<(QAQ o)const{return l<o.l;}
};
int l,r,x,y,n,m,fu,op,seed,vmax,a[N];set<QAQ>s;
inline int rnd(){
Re tmp=seed;
seed=((LL)seed*7+13)%1000000007;
return tmp;
}
inline LL mi(LL a,LL k,LL P){
LL ans=1;a%=P;
while(k){
if(k&1)(ans*=a)%=P;
(a*=a)%=P,k>>=1;
}
return ans;
}
inline IT split(Re w){//分裂
IT it=s.lower_bound(QAQ(w));
if(it!=s.end()&&it->l==w)return it;
--it;
Re L=it->l,R=it->r;LL V=it->v;
s.erase(it);s.insert(QAQ(L,w-1,V));
return s.insert(QAQ(w,R,V)).first;
}
inline void assign(Re l,Re r,LL v){//区间推平
IT itr=split(r+1),itl=split(l);
s.erase(itl,itr),s.insert(QAQ(l,r,v));
}
inline void add(Re l,Re r,LL v){//区间加
IT itr=split(r+1),itl=split(l);
while(itl!=itr)itl->v+=v,++itl;
}
inline LL mi_sum(Re l,Re r,Re k,Re P){//区间幂次和
IT itr=split(r+1),itl=split(l);LL ans=0;
while(itl!=itr)(ans+=(LL)(itl->r-itl->l+1)%P*mi(itl->v,(LL)k,(LL)P)%P)%=P,++itl;
return ans;
}
inline LL ask_k(Re l,Re r,Re k){//区间第k大
IT itr=split(r+1),itl=split(l);
vector<pair<LL,int> >Q;
Q.clear();
while(itl!=itr)Q.push_back(pair<LL,int>(itl->v,itl->r-itl->l+1)),++itl;
sort(Q.begin(),Q.end());
for(vector<pair<LL,int> >::iterator it=Q.begin();it!=Q.end();++it){
k-=it->second;
if(k<=0)return it->first;
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&seed,&vmax);
for(Re i=1;i<=n;++i)a[i]=(rnd()%vmax)+1,s.insert(QAQ(i,i,a[i]));
s.insert(QAQ(n+1,n+1,0));
while(m--){
op=(rnd()%4)+1,l=(rnd()%n)+1,r=(rnd()%n)+1;
if(l>r)swap(l,r);
if(op==3)x=(rnd()%(r-l+1))+1;
else x=(rnd()%vmax)+1;
if(op==4)y=(rnd()%vmax)+1;
if(op<2)add(l,r,(LL)x);
else if(op<3)assign(l,r,(LL)x);
else if(op<4)printf("%lld\n",ask_k(l,r,x));
else printf("%lld\n",mi_sum(l,r,x,y));
}
}