寒假集训 | 线段树大杂烩(线段树,主席树,树套树,线段树合并&分裂)解题报告

Update on 2025/2/25

Update on 2025/02/12

题目列表:

SP1043 GSS1 - Can you answer these queries I:静态区间最大子段和

SP1716 GSS3 - Can you answer these queries III:单点修改区间最大子段和

P6327 区间加区间 sin 和:用和差化积把修改展开,维护区间 cos,sin 和

P1908 逆序对:权值线段树入门题,从前往后枚举位置,这个位置的逆序对数就是线段树中 \(> a_i\) 的数的个数。

点击查看 P1908 代码
#include<bits/stdc++.h>
#define int long long 
using namespace std;

inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

int a[1<<20];
unordered_map<int,int> mp;
const int MINN=1;
int MAXN=1e9;
int n;
struct Tree{
    int cnt,lp,rp;
}tree[2000005];

int b[1<<20];
int tot;

void pushup(Tree &p,const Tree &lp,const Tree &rp)
{
    p.cnt=lp.cnt+rp.cnt;
}

void add(int l,int r,const int &x,int &p)
{
    if(!p) p=++tot;

    if(l==r&&l==x)
    {
        tree[p].cnt++;
        return;
    }

    int mid=l+((r-l)>>1);
    if(x<=mid) add(l,mid,x,tree[p].lp);
    if(x>mid) add(mid+1,r,x,tree[p].rp);

    pushup(tree[p],tree[tree[p].lp],tree[tree[p].rp]);
}

int query(int l,int r,const int &sl,const int &sr,int p)
{
    if(!p) return 0;
    if(sl<=l&&r<=sr) return tree[p].cnt;
//    cout<<l<<" "<<r<<"\n";
    int mid=l+((r-l)>>1),lp=tree[p].lp,rp=tree[p].rp,ans=0;

    if(sl<=mid) ans+=query(l,mid,sl,sr,lp);
    if(mid+1<=sr) ans+=query(mid+1,r,sl,sr,rp);
    return ans;
}

signed main()
{
	//mt19937_64 myrand(time(0));
    n=read();
    long long ans=0;
    int root=0;

    for(int i=1;i<=n;i++) b[i]=a[i]=read();
    sort(a+1,a+1+n);
    a[0]=-1;
    a[n+1]=999999999999;
    for(int i=1;i<=n+1;i++)
    if(a[i]!=a[i-1]) mp[a[i]]=++tot;

    MAXN=tot;
    tot=0;

    for(int i=1;i<=n;i++)
    {
        int x=mp[b[i]];
//        cout<<"tot="<<tot<<" x="<<x<<" "<<query(MINN,MAXN,MINN,x,root)<<" ";
        ans+=i-1-query(MINN,MAXN,MINN,x,root);
  //      cout<<ans<<" \n";
        add(MINN,MAXN,x,root);
    }
//    cout<<"\n";

    write(ans);
	return 0;
}

P1637 三元上升子序列:权值线段树更麻烦一点的入门题。枚举中间位置 j,在 j 前面的数建权值线段树 1,在 j 后面的数建权值线段树 2,答案显然。

点击查看 P1637 代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

int n;
int a[1<<20];
const int MINN=0,MAXN=1e5;

struct Node{
	struct Tree{
		int lp,rp,sum;
	}tree[2000005];
	int tot=0;
	int root=0;

	void pushup(Tree &p,Tree lp,Tree rp)
	{	
		p.sum=lp.sum+rp.sum;
	}

	void add(int l,int r,int x,int op,int &p)
	{
		if(!p) p=++tot;
		if(l==r&&l==x)
		{
			tree[p].sum+=op;
			return;
		}
		int mid=(l+r)>>1;

		if(x<=mid) add(l,mid,x,op,tree[p].lp);
		if(x>mid) add(mid+1,r,x,op,tree[p].rp);
		pushup(tree[p],tree[tree[p].lp],tree[tree[p].rp]);
	}

	int query(int l,int r,int sl,int sr,int p)
	{
		if(!p) return 0;
		if(sl<=l&&r<=sr) return tree[p].sum;

		int mid=(l+r)>>1,lp=tree[p].lp,rp=tree[p].rp,ans=0;
		if(sl<=mid) ans+=query(l,mid,sl,sr,lp);
		if(mid+1<=sr) ans+=query(mid+1,r,sl,sr,rp);
		return ans;
	}
}tree[2];

signed main()
{
	//mt19937_64 myrand(time(0));
	n=read();
	for(int i=1;i<=n;i++) a[i]=read();

	for(int i=2;i<=n;i++) tree[1].add(MINN,MAXN,a[i],1,tree[1].root);

	int ans=0;
	for(int mid=2;mid<n;mid++)
	{
		tree[0].add(MINN,MAXN,a[mid-1],1,tree[0].root);
		tree[1].add(MINN,MAXN,a[mid],-1,tree[1].root);
		ans+=tree[0].query(MINN,MAXN,MINN,a[mid]-1,tree[0].root)*(n-mid-tree[1].query(MINN,MAXN,MINN,a[mid],tree[0].root));
	}	

	write(ans);

	return 0;
}

P3369 【模板】普通平衡树:权值线段树模板题。

点击查看 P3369 代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

const int MAXN=1e7,MINN=-1e7;
int n,tot;
struct Tree{
	int l,r,lp,rp,num;
}tree[1<<23];

void pushup(Tree &p,const Tree &lp,const Tree &rp)
{
	p.num=lp.num+rp.num;
}

void build(int &p,const int &l,const int &r)
{
	p=++tot;
	tree[p].l=l;
	tree[p].r=r;
}

void add(int l,int r,const int &x,const int &op,int &p)
{
	if(!p) build(p,l,r);
	if(l==r)
	{
		tree[p].num+=op;
		tree[p].num=max(tree[p].num,0ll);
		return; 
	}

	int mid=(l+r)>>1;

	if(x<=mid) add(l,mid,x,op,tree[p].lp);
	if(mid+1<=x) add(mid+1,r,x,op,tree[p].rp);

	pushup(tree[p],tree[tree[p].lp],tree[tree[p].rp]);
}

int query(int l,int r,const int &sl,const int &sr,int p)
{
	if(sl>sr) return 0;
	if(!p) return 0;
	if(sl<=l&&r<=sr) return tree[p].num;

	int mid=(l+r)>>1,lp=tree[p].lp,rp=tree[p].rp,ans=0;
	if(sl<=mid) ans+=query(l,mid,sl,sr,lp);
	if(sr>=mid+1) ans+=query(mid+1,r,sl,sr,rp);
	return ans;
}

int kth(int l,int r,int k,int p)
{
	if(!p) return 0;
	if(l==r) return l;

	int mid=(l+r)>>1,lp=tree[p].lp,rp=tree[p].rp;
	if(tree[lp].num>=k) return kth(l,mid,k,lp);
	if(tree[lp].num<k) return kth(mid+1,r,k-tree[lp].num,rp);
	return 0;
}

signed main()
{
	n=read();
	int root=0;
	for(int i=1;i<=n;i++)
	{
		int op=read(),x=read();
		if(op==1) add(MINN,MAXN,x,1,root);
		if(op==2) add(MINN,MAXN,x,-1,root);
		if(op==3) write(query(MINN,MAXN,MINN,x-1,root)+1),putchar('\n');
		if(op==4) write(kth(MINN,MAXN,x,root)),putchar('\n');
		if(op==5) write(kth(MINN,MAXN,query(MINN,MAXN,MINN,x-1,root),root)),putchar('\n');
		if(op==6) write(kth(MINN,MAXN,query(MINN,MAXN,MINN,x,root)+1,root)),putchar('\n');
	}


	return 0;
}

P3834 【模板】可持久化线段树 2:主席树板子(可持久化动态开点权值线段树)

点击查看 P3834 代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

const int MINN=1;
int MAXN;
int tot;
int root[1<<20];
int n,m;
int a[1<<20];
struct Tree{
    int lp,rp,sum;
    int l,r;
}tree[1<<22];
unordered_map<int,int> mp;
int num[1<<20];
int b[1<<20];

void pushup(Tree &p,const Tree &lp,const Tree &rp)
{
    p.sum=lp.sum+rp.sum;
}

void add(int l,int r,int x,int &nwp,int lastp)
{
    if(!nwp) nwp=++tot;
    tree[nwp].l=l;
    tree[nwp].r=r;
    if(l==r&&r==x)
    {
        tree[nwp].sum=tree[lastp].sum+1;
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid) tree[nwp].rp=tree[lastp].rp,add(l,mid,x,tree[nwp].lp,tree[lastp].lp);
    if(x>mid) tree[nwp].lp=tree[lastp].lp,add(mid+1,r,x,tree[nwp].rp,tree[lastp].rp);

    pushup(tree[nwp],tree[tree[nwp].lp],tree[tree[nwp].rp]);
}

int query(int l,int r,int k,int nwp,int lastp)
{
    if(l==r) return l;
    int mid=(l+r)>>1;
    int nw=tree[tree[nwp].lp].sum-tree[tree[lastp].lp].sum;
    if(nw>=k) return query(l,mid,k,tree[nwp].lp,tree[lastp].lp);
    if(nw<k) return query(mid+1,r,k-nw,tree[nwp].rp,tree[lastp].rp);
}

signed main()
{
    n=read();
    m=read();

    for(int i=1;i<=n;i++) b[i]=a[i]=read();

    sort(a+1,a+1+n);
    a[0]=-1;
    a[n+1]=9999999999999;

    for(int i=1;i<=n;i++)
    if(a[i]!=a[i+1]) ++tot,num[tot]=a[i],mp[a[i]]=tot;

    for(int i=1;i<=n;i++) b[i]=mp[b[i]];

    MAXN=tot;

    tot=0;

    for(int i=1;i<=n;i++) add(MINN,MAXN,b[i],root[i],root[i-1]);

    while(m--)
    {
        int l=read(),r=read(),k=read();
        write(num[query(MINN,MAXN,k,root[r],root[l-1])]);
        putchar('\n');
    }
	return 0;
}

P3919 【模板】可持久化线段树 1(可持久化数组):可持久化普通线段树

点击查看 P3919 代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

int m,n;
int a[1<<20];

struct Tree{
    int val,lp,rp;
}tree[1<<25];

int tot;
int root[1<<20];

void build(int l,int r,int &p)
{
    if(!p) p=++tot;
    if(l==r)
    {
        tree[p].val=a[l];
        return;
    }

    int mid=(l+r)>>1;
    build(l,mid,tree[p].lp);
    build(mid+1,r,tree[p].rp);
}

void add(int l,int r,int p,int x,int &nwp,int lastp)
{
    if(!nwp) nwp=++tot;
    if(l==r)
    {
        tree[nwp].val=x;
        return;
    }

    int mid=(l+r)>>1;

    if(p<=mid) tree[nwp].rp=tree[lastp].rp,add(l,mid,p,x,tree[nwp].lp,tree[lastp].lp);
    if(p>mid) tree[nwp].lp=tree[lastp].lp,add(mid+1,r,p,x,tree[nwp].rp,tree[lastp].rp);
}

int query(int l,int r,const int &p,int nwp)
{
    if(l==r) return tree[nwp].val;
    int mid=(l+r)>>1;

    if(p<=mid) return query(l,mid,p,tree[nwp].lp);
    return query(mid+1,r,p,tree[nwp].rp);
}

signed main()
{
    n=read();
    m=read();

    for(int i=1;i<=n;i++) a[i]=read();
    build(1,n,root[0]);

    for(int i=1;i<=m;i++)
    {
        int v=read(),op=read(),loc=read();
        if(op==1) add(1,n,loc,read(),root[i],root[v]);
        if(op==2) root[i]=root[v],write(query(1,n,loc,root[v])),putchar('\n');
    }

	return 0;
}

P1383 高级打字机:可持久化普通线段树

P3567 [POI 2014] KUR-Couriers:主席树

P7261 [COCI 2009/2010 #3] PATULJCI:P3567 的双倍经验

SP5652 PATULJCI - Snow White and the N dwarfs:P7261 的双倍经验

P7252 [JSOI2011] 棒棒糖:P7261 的双倍经验

P2617 Dynamic Rankings:树套树的简单模板题(树状数组套普通动态开点权值线段树)

P1533 可怜的狗狗:主席树

SP3946 MKTHNUM - K-th Number:主席树(静态区间第 k 小)

P3380 【模板】树套树:树套树的模板题(树状数组套普通动态开点权值线段树)

点击查看 P3380 代码
#include<bits/stdc++.h>
#define int long l
using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

int n,m;
int a[1<<20];
int tot;
struct Tree{
    int cnt,lp,rp;
}tree[1<<24];
int root[1<<20];
const int MINN=0,MAXN=1e8;
int cnt[2],tmp[2][105];

int lowbit(int x) { return x&(-x); }

void pushup(Tree &p,Tree lp,Tree rp)
{
    p.cnt=lp.cnt+rp.cnt;
    if(p.cnt<0) p.cnt=0;
}

void add(int l,int r,int val,const int op,int &p)
{
    if(!p) p=++tot;
//    tree[p].cnt+=op;
//    if(tree[p].cnt<0) tree[p].cnt=0;

    if(l==r)
    {
        tree[p].cnt+=op;
        if(tree[p].cnt<0) tree[p].cnt=0;
        return;
    }

    int mid=(l+r)>>1;
    if(val<=mid) add(l,mid,val,op,tree[p].lp);
    else add(mid+1,r,val,op,tree[p].rp);

    pushup(tree[p],tree[tree[p].lp],tree[tree[p].rp]);
}

void prepare_add(int root_id,int val,const int op)
{
    for(int i=root_id;i<=n;i+=lowbit(i))
    add(MINN,MAXN,val,op,root[i]);
}

void prepare(int lroot,int rroot)
{
    memset(tmp,0,sizeof(tmp));
    cnt[0]=cnt[1]=0;

    for(int i=lroot;i>0;i-=lowbit(i)) tmp[0][++cnt[0]]=root[i];
    for(int i=rroot;i>0;i-=lowbit(i)) tmp[1][++cnt[1]]=root[i];
}

int query(int l,int r,int x)//x 的排名
{
    if(l==r) return 1;

    int mid=(l+r)>>1;
    int nw=0;
    if(x<=mid) 
    {
        for(int i=1;i<=cnt[1];i++) tmp[1][i]=tree[tmp[1][i]].lp;
        for(int i=1;i<=cnt[0];i++) tmp[0][i]=tree[tmp[0][i]].lp;
        return query(l,mid,x);
    }
    for(int i=1;i<=cnt[1];i++) nw+=tree[tree[tmp[1][i]].lp].cnt,tmp[1][i]=tree[tmp[1][i]].rp;
    for(int i=1;i<=cnt[0];i++) nw-=tree[tree[tmp[0][i]].lp].cnt,tmp[0][i]=tree[tmp[0][i]].rp;

    return nw+query(mid+1,r,x);
}

int kth(int l,int r,int k)//排名为 k 的值
{
    if(k<1) return -2147483647;
//    if() return 2147483647;

    if(l==r) return l;

    int mid=(l+r)>>1,nw=0;

    for(int i=1;i<=cnt[1];i++) nw+=tree[tree[tmp[1][i]].lp].cnt;
    for(int i=1;i<=cnt[0];i++) nw-=tree[tree[tmp[0][i]].lp].cnt;

    if(nw>=k)
    {
        for(int i=1;i<=cnt[1];i++) tmp[1][i]=tree[tmp[1][i]].lp;
        for(int i=1;i<=cnt[0];i++) tmp[0][i]=tree[tmp[0][i]].lp;
        return kth(l,mid,k);
    }
    else
    {
        for(int i=1;i<=cnt[1];i++) tmp[1][i]=tree[tmp[1][i]].rp;
        for(int i=1;i<=cnt[0];i++) tmp[0][i]=tree[tmp[0][i]].rp;
        return kth(mid+1,r,k-nw);
    }    
}

signed main()
{
    n=read();
    m=read();
    for(int i=1;i<=n;i++) a[i]=read(),prepare_add(i,a[i],1);

    while(m--)
    {
        int op=read();
        if(op==3)
        {
            int pos=read(),k=read();
            prepare_add(pos,a[pos],-1);
            a[pos]=k;
            prepare_add(pos,a[pos],1);
            continue;
        }
        int l=read(),r=read(),k=read(),nw=0;
        prepare(l-1,r);

        if(op==1) write(query(MINN,MAXN,k));
        if(op==2) write(kth(MINN,MAXN,k));
        if(op==4) nw=query(MINN,MAXN,k)-1,prepare(l-1,r),write(kth(MINN,MAXN,nw));
        if(op==5) nw=query(MINN,MAXN,k+1),prepare(l-1,r),write(((nw<=(r-l+1))?kth(MINN,MAXN,nw):2147483647));
        putchar('\n');
    }
	//mt19937_64 myrand(time(0));
	return 0;
}

P3835 【模板】可持久化平衡树:树套树的模板题(树状数组套普通动态开点权值线段树)

代码与 P3380 极为类似。

点击查看 P3835 代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

const int MINN=-2147483647,MAXN=2147483647;
int n;
int root[1<<20];

struct Tree{
    int cnt,lp,rp;
}tree[32000005];
int tot;

void pushup(Tree &p,Tree lp,Tree rp)
{
    p.cnt=lp.cnt+rp.cnt;
}

void add(int l,int r,int val,const int op,int &nwp,int lastp)
{
    if(!nwp) nwp=++tot;
    if(l==r)
    {
        tree[nwp].cnt+=tree[lastp].cnt+op;
        if(tree[nwp].cnt<0) tree[nwp].cnt=0;
        return;
    }

    int mid=(l+r)>>1;
    if(val<=mid) tree[nwp].rp=tree[lastp].rp,add(l,mid,val,op,tree[nwp].lp,tree[lastp].lp);
    else tree[nwp].lp=tree[lastp].lp,add(mid+1,r,val,op,tree[nwp].rp,tree[lastp].rp);

    pushup(tree[nwp],tree[tree[nwp].lp],tree[tree[nwp].rp]);
}

int query(int l,int r,int x,int p) //查询 x 的排名
{
//    if(!p) return 0;
    if(l==r) return 1;
    int mid=(l+r)>>1;
    if(x<=mid) return query(l,mid,x,tree[p].lp);
    else return tree[tree[p].lp].cnt+query(mid+1,r,x,tree[p].rp);
}

int kth(int l,int r,int k,int p)
{
//    if(k<1) return -2147483647;
    if(l==r) return l;

    int mid=(l+r)>>1;
    int nw=tree[tree[p].lp].cnt;
    if(k<=nw) return kth(l,mid,k,tree[p].lp);
    else return kth(mid+1,r,k-nw,tree[p].rp);
}

signed main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        int v=read(),op=read(),x=read();
        if(op==1)
        {
            add(MINN,MAXN,x,1,root[i],root[v]);
            continue;
        }
        if(op==2)
        {
            add(MINN,MAXN,x,-1,root[i],root[v]);
            continue;
        }
        root[i]=root[v];
        if(op==3) write(query(MINN,MAXN,x,root[i]));
        if(op==4) write(kth(MINN,MAXN,x,root[i]));
        if(op==5) write(kth(MINN,MAXN,query(MINN,MAXN,x,root[i])-1,root[i]));
        if(op==6) write(kth(MINN,MAXN,query(MINN,MAXN,x+1,root[i]),root[i]));/*nw=query(MINN,MAXN,x+1,root[i]),cout<<"nw="<<nw<<"   ",write(((nw>tree[root[i]].cnt)?kth(MINN,MAXN,nw,root[i]):2147483647));*/
        putchar('\n');
    }
    // for(int i=1;i<=n;i++)cout<<root[i]<<" ";
    // cout<<"\n";
	//mt19937_64 myrand(time(0));
	return 0;
}

P6166 [IOI 2012] scrivener:P1383 的双倍经验

P1972 [SDOI2009] HH的项链:使用主席树可以在线处理

P4587 [FJOI2016] 神秘数:主席树

点击查看 P4587 代码
#include<bits/stdc++.h>
#define int long l
using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

int n;
int a[1<<20];
int root[1<<20];
int tot;
const int MINN=-2,MAXN=1e9+2;
struct Tree{
    int sum,cnt,lp,rp,l,r;
}tree[100005*32];

void pushup(Tree &p,Tree lp,Tree rp)
{
    p.cnt=lp.cnt+rp.cnt;
	p.sum=lp.sum+rp.sum;
}

void add(int l,int r,int val,int &p,int lastp)
{
    if(!p) p=++tot;
    tree[p].l=l;
	tree[p].r=r;
	if(l==r)
    {
        tree[p].cnt=tree[lastp].cnt+1;
        tree[p].sum=tree[lastp].sum+val;
        return;
    }

	int mid=(l+r)>>1;
	if(val<=mid) tree[p].rp=tree[lastp].rp,add(l,mid,val,tree[p].lp,tree[lastp].lp);
	else tree[p].lp=tree[lastp].lp,add(mid+1,r,val,tree[p].rp,tree[lastp].rp);

	pushup(tree[p],tree[tree[p].lp],tree[tree[p].rp]);
}

int getsum(int l,int r,int val,int p,int lastp)//<=val ????
{
	if(l==r) return tree[p].sum-tree[lastp].sum;
	int mid=(l+r)>>1;
	if(val<=mid) return getsum(l,mid,val,tree[p].lp,tree[lastp].lp);
	else return tree[tree[p].lp].sum-tree[tree[lastp].lp].sum+getsum(mid+1,r,val,tree[p].rp,tree[lastp].rp);
}

int kth(int l,int r,int k,int p,int lastp)//??? k ???
{
	if(l==r) return l;
	int mid=(l+r)>>1;
	int nw=tree[tree[p].lp].cnt-tree[tree[lastp].lp].cnt;
	if(nw>=k) return kth(l,mid,k,tree[p].lp,tree[lastp].lp);
	else return kth(mid+1,r,k-nw,tree[p].rp,tree[lastp].rp);
}	

int query(int l,int r,int val,int p,int lastp)//val ???
{
	if(l==r) return 1;
	int mid=(l+r)>>1;
	if(val<=mid) return query(l,mid,val,tree[p].lp,tree[lastp].lp);
	else return tree[tree[p].lp].cnt-tree[tree[lastp].lp].cnt+query(mid+1,r,val,tree[p].rp,tree[lastp].rp);
}

signed main()
{
	//mt19937_64 myrand(time(0));
//	freopen("data.out","r",stdin);
//	freopen("ans2.out","w",stdout);

	n=read();
	for(int i=1;i<=n;i++) a[i]=read(),add(MINN,MAXN,a[i],root[i],root[i-1]);

	int m=read();

// 	for(int i=0;i<=tot;i++)
// 	{
//		cout<<"id="<<i<<" tree[p].l="<<tree[i].l<<" tree[p].r="<<tree[i].r<<" btree[i].lp= "<<tree[i].lp<<" "<<"tree[i].rp="<<tree[i].rp<<" tree[i].cnt="<<tree[i].cnt<<" tree[i].sum="<<tree[i].sum<<"\n";
// 		if(tree[i].lp+tree[i].rp==0) cout<<"\n";
//	}

	while(m--)
	{
		int l=read(),r=read();
//		int k=read();

		int nw=1;

		while(1)
		{
			int nww=getsum(MINN,MAXN,nw,root[r],root[l-1]);
/*这行是和题解拍出来的*/while(nww!=nw) nw=nww,nww=getsum(MINN,MAXN,nw,root[r],root[l-1])/*,cout<<"m="<<m<<"nw="<<nw<<"\n"*/;
		//	cout<<"m="<<m<<" nw="<<nw<<" kth="<<kth(MINN,MAXN,query(MINN,MAXN,nw,root[r],root[l-1]),root[r],root[l-1])<<"\n";
			nw++;
			if(nw!=kth(MINN,MAXN,query(MINN,MAXN,nw,root[r],root[l-1]),root[r],root[l-1]))
			break;
		}
		write(nw);
		putchar('\n');

		// cout<<kth(MINN,MAXN,k,root[r],root[l-1])<<" ";
		// cout<<query(MINN,MAXN,k,root[r],root[l-1])<<" ";
		// cout<<getsum(MINN,MAXN,k,root[r],root[l-1])<<" \n";
	}

	return 0;
}
/*

5
1 4 2 3 5
4
1 5 1
2 3 2
2 4 1 
3 5 1

*/

P1110 [ZJOI2007] 报表统计:史,需要将普通权值线段树空间压缩到极致才能卡着 0.5 Mib 过,(好像假了)懒得写优化了(咕)。

P2343 宝石管理系统:普通动态开点权值线段树,带修全局第 k 大

P8701 [蓝桥杯 2019 国 B] 第八大奇迹:带修区间第 k 大,树套树实现

P3224 [HNOI2012] 永无乡:(权值)线段树合并简单模板题,如果两棵线段树上两个节点都存在,则继续递归,边界为有 \(\ge 1\) 棵树没有节点或当前节点为叶子节点,则不再递归。

点击查看 P3224 代码 ```cpp #include #define int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],p1=buf,p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;

define getchar() \

(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)
? EOF
: ss++)
char In[1<<20],
ss=In,*tt=In;
inline int read()
{
int x=0,c=getchar(),f=0;
for(;c>'9'||c<'0';f=c=='-',c=getchar());
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
return f?-x:x;
}
inline char readc()
{
char c=getchar();
for(;c<'A'||c>'Z';c=getchar());
return c;
}
inline void write(int x)
{
if(x<0) x=-x,putchar('-');
if(x>9) write(x/10);
putchar(x%10+'0');
}

const int MINN=-1,MAXN=3e5+1;
int n,m;
int p[1<<20];
int root[1<<20];
struct Tree{
int lp,rp,cnt;
}tree[300005*19];
int tot;

int fa[1<<20];
int find(int x)
{
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);
}

void pushup(Tree &p,Tree lp,Tree rp)
{
p.cnt=lp.cnt+rp.cnt;
}

void add(int l,int r,int val,int &p)
{
if(!p) p=++tot;
if(l==r)
{
tree[p].cnt++;
return;
}
int mid=(l+r)>>1;
if(val<=mid) add(l,mid,val,tree[p].lp);
else add(mid+1,r,val,tree[p].rp);

pushup(tree[p],tree[tree[p].lp],tree[tree[p].rp]);

}

void merge(int l,int r,int p1,int p2)//将 p2 合并到 p1 里面
{
// if(p10&&p20) return;
if(l==r) { tree[p1].cnt+=tree[p2].cnt; return; }
int mid=(l+r)>>1;
if(tree[p1].lp&&tree[p2].lp) merge(l,mid,tree[p1].lp,tree[p2].lp);
if(tree[p1].rp&&tree[p2].rp) merge(mid+1,r,tree[p1].rp,tree[p2].rp);

if(tree[p1].lp==0&&tree[p2].lp) tree[p1].lp=tree[p2].lp;//注意 if 顺序
if(tree[p1].rp==0&&tree[p2].rp) tree[p1].rp=tree[p2].rp;

pushup(tree[p1],tree[tree[p1].lp],tree[tree[p1].rp]);

}

int kth(int l,int r,int k,int p)
{
if(l==r) return l;
int mid=(l+r)>>1,lp=tree[p].lp,rp=tree[p].rp;
int nw=tree[lp].cnt;
if(nw>=k) return kth(l,mid,k,lp);
else return kth(mid+1,r,k-nw,rp);
}

signed main()
{
//mt19937_64 myrand(time(0));

n=read();
m=read();
for(int i=1;i<=n;i++)
{
	int x=read();
	p[x]=i;

	add(MINN,MAXN,x,root[i]);
	fa[i]=i;
}
for(int i=1;i<=m;i++)
{
	int u=read(),v=read();
	int fx=find(u),fy=find(v);

	if(fx==fy) continue;
	fa[fy]=fx;
	merge(MINN,MAXN,root[fx],root[fy]);
}

int q=read();
while(q--)
{
	char op=readc();
	int x=read(),y=read();

//	cout<<7-q<<" "<<op<<" "<<x<<" "<<y<<" \n";
	int fx=find(x);
	if(op=='Q')
	{
		int nw=kth(MINN,MAXN,y,root[fx]);
		write(((nw==MAXN)?-1:p[nw]));
		putchar('\n');
		continue;
	}
	if(op=='B')
	{	
		int fy=find(y);
		if(fx==fy) continue;
		fa[fy]=fx;
		merge(MINN,MAXN,root[fx],root[fy]);
	}
}

return 0;

}



</details>

[P4513 小白逛公园](https://www.luogu.com.cn/problem/P4513):单点修改区间最大子段和

[P5494 【模板】线段树分裂](https://www.luogu.com.cn/problem/P5494):(权值)线段树分裂模板题


<details>
<summary>点击查看代码</summary>

```cpp
#include<bits/stdc++.h>
#define int long l
using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

const int MINN=-10,MAXN=1e7+7;
int root[1<<20];
struct Tree{
	int lp,rp,cnt;
}tree[32000005];
int tot;

void pushup(Tree &p,Tree lp,Tree rp)
{
	p.cnt=lp.cnt+rp.cnt;
}

void add(int l,int r,int val,int x,int &p)
{
	if(!p) p=++tot;
	if(l==r)
	{
		tree[p].cnt+=x;
		return;
	}

	int mid=(l+r)>>1;
	if(val<=mid) add(l,mid,val,x,tree[p].lp);
	else add(mid+1,r,val,x,tree[p].rp);

	pushup(tree[p],tree[tree[p].lp],tree[tree[p].rp]);
}

// 将 p2 合并到 p1 里
void merge(int l,int r,int &p1,int &p2)
{
	if(!p1) p1=++tot;

	if(l==r)
	{
		tree[p1].cnt+=tree[p2].cnt;
		return;
	}

	int mid=(l+r)>>1;

	if(tree[p1].lp&&tree[p2].lp) merge(l,mid,tree[p1].lp,tree[p2].lp);
	else if(tree[p2].lp) tree[p1].lp=tree[p2].lp,tree[p2].lp=0;
	
	if(tree[p1].rp&&tree[p2].rp) merge(mid+1,r,tree[p1].rp,tree[p2].rp);
	else if(tree[p2].rp) tree[p1].rp=tree[p2].rp,tree[p2].rp=0;

	pushup(tree[p1],tree[tree[p1].lp],tree[tree[p1].rp]);
	pushup(tree[p2],tree[tree[p2].lp],tree[tree[p2].rp]); 
}

//将 [sl,sr] 从 p2 中分离
void spilt(int l,int r,int sl,int sr,int &p1,int &p2)
{
	if(sl<=l&&r<=sr)
	{
		p1=p2;
		p2=0;
		tree[p2]={0,0,0};
		return;
	}
	if(!p1) p1=++tot;

	int mid=(l+r)>>1;
	if(sl<=mid) spilt(l,mid,sl,sr,tree[p1].lp,tree[p2].lp);
	if(sr>mid) spilt(mid+1,r,sl,sr,tree[p1].rp,tree[p2].rp);

	pushup(tree[p1],tree[tree[p1].lp],tree[tree[p1].rp]);
	pushup(tree[p2],tree[tree[p2].lp],tree[tree[p2].rp]); 
}

int query(int l,int r,int x,int p)
{
	if(l==r) return 1;
	int mid=(l+r)>>1,lp=tree[p].lp,rp=tree[p].rp;
	if(x<=mid) return query(l,mid,x,lp);
	else return tree[lp].cnt+query(mid+1,r,x,rp);
}

int kth(int l,int r,int k,int p)
{
	if(l==r) return l;

	int mid=(l+r)>>1,lp=tree[p].lp,rp=tree[p].rp;
	int nw=tree[lp].cnt;
	if(nw>=k) return kth(l,mid,k,lp);
	else return kth(mid+1,r,k-nw,rp);
}

int n,m;
int a[1<<20];


signed main()
{
	//mt19937_64 myrand(time(0));
//	freopen("P5494_3.in","r",stdin);
	n=read();
	m=read();

	int nw=1;
	for(int i=1;i<=n;i++) a[i]=read(),add(MINN,MAXN,i,a[i],root[1]);

	while(m--)
	{
	//	cout<<tree[0].cnt<<"\n\n\n";
		int op=read();
		if(op==0)
		{
			int p=read(),x=read(),y=read();
			nw++;
			spilt(MINN,MAXN,x,y,root[nw],root[p]);
		}
		if(op==1)
		{
			int p=read(),t=read();
			merge(MINN,MAXN,root[p],root[t]);
		//	root[t]=0;
		}
		if(op==2)
		{
			int p=read(),x=read(),q=read();
			add(MINN,MAXN,q,x,root[p]);
		}
		if(op==3)
		{
			int p=read(),x=read(),y=read();
			int nw=query(MINN,MAXN,y+1,root[p])-query(MINN,MAXN,x,root[p]);
			write(nw);
			putchar('\n');

			if(nw==823) {cout<<op<<" "<<"\n\n\n";break;}
		}
		if(op==4)
		{
			int p=read(),k=read();
			int nw=kth(MINN,MAXN,k,root[p]);
			if(nw==MAXN||nw==MINN) nw=-1;
			write(nw);
			putchar('\n');
			if(nw==823) {cout<<op<<" "<<"\n\n\n"; break;}
		}
	}

	return 0;
}

/*

10 11
1 1 1 0 1 1 0 1 1 1 

0 1 2 5 
2 2 3 4
3 2 1 5 
3 2 1 4 
3 2 3 4 
3 2 4 4 
4 1 3 
4 2 5 
4 1 8 
4 2 10 
4 2 0



*/

//1: 1 0 0 0 0 1 0 1 1 1 
//2: 0 1 1 0 1 0 0 0 0 0  
//-> 0 1 1 3 1 0 0 0 0 0   
//   1 2 3 4 5 6 7 8 9 10
/*

0 1 2 5 
2 2 3 4
3 2 1 5 -> 6
3 2 1 4 -> 5
3 2 3 4 -> 4
3 2 4 4 -> 3
4 1 3 -> 8
4 2 5 -> 4
4 1 8 -> -1
4 2 10 -> -1
4 2 0 -> -1

*/


P3168 [CQOI2015] 任务查询系统:神仙主席树题(差分后可看作单点修改,按时间从小到大排序后分配编号,再跑主席树)(树套树也可做,第一次写的是树套树,发现没有主席树好维护写挂了不会调,于是改写主席树)


Update on 2025/2/25

P2633 Count on a tree:树上维护主席树,对根节点到这个节点路径上的权值建主席树, add(MINN,MAXN,a[p],root[p],root[fa]);查询明天再写

查询时运用前缀和,\(u\)\(v\) 路径上主席树(查询区间第 \(k\) 小)可表示为 \(u+v-lca(u,v)-fa[lca(u,v)]\)

感性理解一下

点击查看 P2633 代码

#include<bits/stdc++.h>
#ne int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

// int n,m,s;
const int MAXN=pow(2,31),MINN=0;

vector<int> E[1<<20];
int f[1<<20][22];
void add(int u,int v) { E[u].push_back(v); }

int n,m;
int deep[1<<20];
int a[1<<20];

int lca(int x,int y);
struct S_tree{
    int root[1<<20];
    int tot;
    struct Tree{
        int lp,rp,cnt;
    }tree[100005*33];
    int tmp[3][3],cnt[3];

    void pushup(Tree &p,Tree lp,Tree rp)
    {
        p.cnt=lp.cnt+rp.cnt;
    }

    void add(int l,int r,int val,int &p,int lastp)
    {
        if(!p) p=++tot;
        if(l==r)
        {
            tree[p].cnt=tree[lastp].cnt+1;
            return;
        }

        int mid=(l+r)>>1;
        if(val<=mid) tree[p].rp=tree[lastp].rp,add(l,mid,val,tree[p].lp,tree[lastp].lp);
        else tree[p].lp=tree[lastp].lp,add(mid+1,r,val,tree[p].rp,tree[lastp].rp);

        pushup(tree[p],tree[tree[p].lp],tree[tree[p].rp]);
    }

    void prepare(int x,int y)
    {
        memset(tmp,0,sizeof(tmp));
        cnt[0]=cnt[1]=2;

        tmp[1][1]=root[x];
        tmp[1][2]=root[y];
        int nw=lca(x,y);
        tmp[0][1]=root[nw];
        tmp[0][2]=root[f[nw][0]];
    }

    int kth(int l,int r,int k)
    {
        if(l==r) return l;
        int mid=(l+r)>>1,nw=0;
        for(int i=1;i<=cnt[1];i++) nw+=tree[tree[tmp[1][i]].lp].cnt;
        for(int i=1;i<=cnt[0];i++) nw-=tree[tree[tmp[0][i]].lp].cnt;

        if(nw>=k)
        {
            for(int i=1;i<=cnt[1];i++) tmp[1][i]=tree[tmp[1][i]].lp;
            for(int i=1;i<=cnt[0];i++) tmp[0][i]=tree[tmp[0][i]].lp;
            return kth(l,mid,k);
        }
        else
        {
            for(int i=1;i<=cnt[1];i++) tmp[1][i]=tree[tmp[1][i]].rp;
            for(int i=1;i<=cnt[0];i++) tmp[0][i]=tree[tmp[0][i]].rp;
            return kth(mid+1,r,k-nw);
        }
    }

    int query_kth(int x,int y,int k)
    {
        prepare(x,y);
        return kth(MINN,MAXN,k);
    }
}tree;

void dfs(int p,int fa)
{
    tree.add(MINN,MAXN,a[p],tree.root[p],tree.root[fa]);
	deep[p]=deep[fa]+1;
	f[p][0]=fa;
	for(int i=0;i<E[p].size();i++)
	{
		int to=E[p][i];
		if(to==fa) continue;
		dfs(to,p);
	}
}

void init()
{
	for(int k=1;k<=20;k++)
	for(int i=1;i<=n;i++)
	f[i][k]=f[f[i][k-1]][k-1];
}

int lca(int x,int y)
{
	if(deep[x]<deep[y]) swap(x,y);

	for(int k=20;k>=0;k--)
	{
		if(deep[f[x][k]]>=deep[y])
		{
			x=f[x][k];
		}
	}
	if(x==y) return x;
	for(int k=20;k>=0;k--)
	{
		if(f[x][k]!=f[y][k])
		{
			x=f[x][k];
			y=f[y][k];
		}
	}
	return f[x][0];
}

signed main()
{
	n=read();
	m=read();
    for(int i=1;i<=n;i++) a[i]=read();
	// root=read();
    // root=1;

	for(int i=1;i<n;i++)
	{
		int x=read(),y=read();
		add(x,y);
		add(y,x);
	}

	dfs(1,0);
	init();

    int last=0;
	while(m--)
    {
        int u=read()^last,v=read(),k=read();
        last=tree.query_kth(u,v,k);
        write(last);
        putchar('\n');
    }
	// {
	// 	int a=read(),b=read();
	// 	write(lca(a,b));
	// 	putchar('\n');
	// }

	//mt19937_64 myrand(time(0));
	return 0;
}




Update on 2025/3/17

P3302 [SDOI2013] 森林
操作:动态 lca + 树上主席树(树上主席树合并)

查询与 P2633 一样,关键看合并怎么写。

每次联边 (u,v) 时比较 u 所在树与 v 所在树节点个数(大小),将小的合并进大的,暴力重构小树中的主席树。

我们还需要求 lca,可以树链剖分,每次暴力重构小树中与树链剖分有关的数组,时间复杂度优秀,为 O(siz)。

点击查看 P3302 代码

#include<bits/stdc++.h>
#ne int long long

using namespace std;

const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar()                                                              \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt)     \
	? EOF                                                                 \
	: *ss++)
char In[1<<20],*ss=In,*tt=In;

inline int read()
{
	int x=0,c=getchar(),f=0;
	for(;c>'9'||c<'0';f=c=='-',c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+(c^48);
	return f?-x:x;
}inline int readc()
{
	int c=getchar();
	for(;c<'A'||c>'Z';c=getchar());
	return c;
}
inline void write(int x)
{
	if(x<0) x=-x,putchar('-');
	if(x>9)  write(x/10);
	putchar(x%10+'0');
}

int n,m,q;
int ROOT;
int a[1<<20];
bool FF=0;
const int MINN=0,MAXN=1e9+1;

vector<int> E[1<<20];
void aaadd(int x,int y) { E[x].push_back(y); }

struct BCJ{

    int father[1<<20];
    int siz[1<<20];

    void init()
    {
        for(int i=0;i<(1<<20);i++)
        {
            father[i]=i;
            siz[i]=1;
        }
    }

    int find(int x)
    {
        if(x==father[x]) return x;
        return father[x]=find(father[x]);
    } 

    void merge(int x,int y)
    {
        int fx=find(x),fy=find(y);
        if(fx==fy) return;

        father[fy]=fx;
        siz[fx]+=siz[fy];
    }
}bcj;

struct SLPF{

    int f[1<<20];
    int siz[1<<20];
    int top[1<<20],son[1<<20],deep[1<<20];

    void dfs2(int p,int tp)
    {
        top[p]=tp;
        if(son[p]) dfs2(son[p],tp);
    
        for(int i=0;i<E[p].size();i++)
        {
            int to=E[p][i];
            if(to==f[p]||to==son[p]) continue;
    
            dfs2(to,to);
        }
    }
    
    int lca(int u,int v)
    {
        while(top[u]!=top[v])
        {
            if(deep[top[v]]>deep[top[u]]) swap(u,v);
            u=f[top[u]];
        }
        return deep[u]<deep[v]?u:v;
    }
}slpf;

struct S_Tree{
    int root[1<<20];
    int cnt[2];
    int tmp[2][4];
    int tot=0;
    struct Tree{
        int lp,rp,cnt;
    }tree[15000001];
    // int st[100005*101],tp;
    // {

    // }

    void pushup(Tree &p,Tree lp,Tree rp)
    {
        p.cnt=lp.cnt+rp.cnt;
    }

    void add(int l,int r,int val,int op,int &p,int lastp)
    {
        // if(!p) p=NewNode();
        if(!p) p=++tot;
        tree[p].cnt=0;
        // tree[tree[p].lp].cnt=0;
        // tree[tree[p].rp].cnt=0;
        if(l==r)
        {
            tree[p].cnt=tree[lastp].cnt+op;
            return;
        }

        int mid=(l+r)>>1;
        if(val<=mid) tree[p].rp=tree[lastp].rp,add(l,mid,val,op,tree[p].lp,tree[lastp].lp);
        else tree[p].lp=tree[lastp].lp,add(mid+1,r,val,op,tree[p].rp,tree[lastp].rp);

        pushup(tree[p],tree[tree[p].lp],tree[tree[p].rp]);
        // cout<<"p="<<p<<" cnt="<<tree[p].cnt<<"\n";
    }

    void prepare(int u,int v,int lca,int fa_lca)
    {
        cnt[0]=cnt[1]=2;
        tmp[1][0]=root[u];
        tmp[1][1]=root[v];
        tmp[0][0]=root[lca];
        tmp[0][1]=root[fa_lca];

        // cout<<u<<" "<<v<
    }

    int kth(int l,int r,int k)
    {
        // if(FF) cout<<"l="<<l<<" r="<<r<<" k="<<k<<" ";
        if(l==r) return l;
        int mid=(l+r)>>1,nw=0;

        for(int i=0;i<cnt[1];i++) nw+=tree[tree[tmp[1][i]].lp].cnt;
        for(int i=0;i<cnt[0];i++) nw-=tree[tree[tmp[0][i]].lp].cnt;

        // if(FF) cout<<"nw="<<nw<<"\n";

        if(nw>=k)
        {
            for(int i=0;i<cnt[1];i++) tmp[1][i]=tree[tmp[1][i]].lp;
            for(int i=0;i<cnt[0];i++) tmp[0][i]=tree[tmp[0][i]].lp;
            return kth(l,mid,k);
        }
        else
        {
            for(int i=0;i<cnt[1];i++) tmp[1][i]=tree[tmp[1][i]].rp;
            for(int i=0;i<cnt[0];i++) tmp[0][i]=tree[tmp[0][i]].rp;
            return kth(mid+1,r,k-nw);
        }
    }
}tree;

    
void dfs1(int p,int fa)
{
    // cout<<p<<" "<<fa<<" \n";
    // tree.root[p]=0;
    slpf.son[p]=0;
    slpf.f[p]=fa;
    slpf.siz[p]=1;
    // slpf.top[p]=0;
    if(FF) cout<<fa<<" "<<p<<"\n";
    slpf.deep[p]=slpf.deep[fa]+1;
    tree.add(MINN,MAXN,a[p],1,tree.root[p],tree.root[fa]);

    int maxn=-1;
    for(int i=0;i<E[p].size();i++)
    {
        int to=E[p][i];
        if(to==fa) continue;

        dfs1(to,p);

        slpf.siz[p]+=slpf.siz[to];
        if(slpf.siz[to]>maxn)
        {
            maxn=slpf.siz[to];
            slpf.son[p]=to;
        }
    }
}


signed main()
{
    // freopen("a.in","r",stdin);
	// mt19937_64 myrand(time(0));
    int OOOOOOOOOOOOOOOOOOOOOOOOO=read();

    bcj.init();

    n=read();
    m=read();
    q=read();

    for(int i=1;i<=n;i++) a[i]=read();

    for(int i=1;i<=m;i++)
    {
        int u=read(),v=read();
        // ROOT=u;
        aaadd(u,v);
        aaadd(v,u);
        int fx=bcj.find(u);
        int fy=bcj.find(v);
        // if(bcj.siz[fy]>bcj.siz[fx]) swap(fy,fx),swap(x,y);

        bcj.father[fy]=bcj.father[fx];
        bcj.siz[fx]+=bcj.siz[fy];
    }
    // dfs1(ROOT,0);
    // slpf.dfs2(ROOT,ROOT);
    for(int i=1;i<=n;i++) if(!slpf.deep[i]) dfs1(i,0),slpf.dfs2(i,i);

    int lastans=0;
    while(q--)
    // for(int i=1;i<=1;i++)
    {
        char c=readc();
        // cout<<c<<" ";
        if(c=='Q')
        {
            int x=read(),y=read(),k=read();
            x^=lastans;
            y^=lastans;
            k^=lastans;
            // cout<<"q="<<20-q<<" "<<x<<" "<<y;
            int lca=slpf.lca(x,y);
            int fa_lca=slpf.f[lca];

            // cout<<" "<<lca<<" "<<fa_lca<<" "<<k<<"\n";
            // if(q==6) FF=1;

            tree.prepare(x,y,lca,fa_lca);
            lastans=tree.kth(MINN,MAXN,k);
            // if(lastans==1000000001) FF=1;
            // else FF=0;
            
            // if(FF)cout<<"q="<<20-q<<" "<<x<<" "<<y;
            // if(FF)cout<<" "<<lca<<" "<<fa_lca<<" "<<k<<"\n";
            // if(FF) dfs1(lca,0);
            // if(FF)tree.kth(MINN,MAXN,k);
            cout<<lastans<<"\n";
        }
        else if(c=='L')
        {
            int x=read(),y=read();
            x^=lastans;
            y^=lastans;


            // cout<<"edge: "<<x<<" "<<y<<" \n";

            int fx=bcj.find(x);
            int fy=bcj.find(y);
            if(bcj.siz[fy]>bcj.siz[fx]) {swap(x,y);}

            bcj.father[fy]=fx;
            bcj.siz[fx]+=bcj.siz[fy];
            aaadd(x,y);
            aaadd(y,x);
            
            dfs1(y,x);
            slpf.dfs2(y,y);
        }
    }

	return 0;
}

/*
1
20 12 20

412060525   1 
!42425138    2
!67114752    3
160822495    4
201962681    5
926214957    6
!380263349    7
!733667141    8
!869039239    9
!641017702    10
154667400    11
461702107    12
! 438851950    13
176272938    14
!209229857    15
!985208975    16
762952138    17
!936593832    18
409183276    19
! 999506034   20





2 17
4 1
15 3
3 10
9 10
7 16
19 15
13 2
6 2
3 14
7 18
8 15

Q 6 17 2
Q 762952152 762952154 762952139
L 380263329 380263352
L 380263357 380263333
Q 380263356 380263359 380263348
L 641017709 641017703
L 641017716 641017700
Q 641017716 641017704 641017698
Q 380263359 380263357 380263345
L 733667140 733667136
L 733667150 733667156
L 733667145 733667142
Q 733667149 733667145 733667140
Q 67114772 67114761 67114759

Q 733667145 733667159 733667142
Q 380263359 380263356 380263351
Q 869039233 869039232 869039237
Q 380263359 380263345 380263356
Q 733667136 733667140 733667143
Q 412060537 412060516 412060519

*/


/*
762952138
380263349
641017702
380263349
733667141
67114752

733667141

380263349
869039239
380263349
733667141
412060525
985208975


*/


posted @ 2025-02-12 20:31  Wy_x  阅读(48)  评论(2)    收藏  举报