【算法】可持久化线段树(主席树)+启发式合并+LCA
【题意】给定森林,每次询问u,v路径上的第k小,或给u,v连边(保证森林),n<=8*10^4。
【题解】
区间第k小:离散化,在上一个位置的基础上建可持久化权值线段树,每次比较左子树和k并找到第一个大于等于k的位置。
树上第k小:每个点上其父亲的基础上建树,ans=ask(u)+ask(v)-ask(lca(u,v))-ask(fa[lca(u,v)]),恰好是不重不漏的一条链。
连边:因为保证森林,只要使用启发式合并的方法,对点数少的树DFS重建树即可,启发式合并保证每个点至多重建log n次。
复杂度O(n log2n)。
注意:数据中第一行testcase为数据编号,不是组数。
注意:空间开大!空间复杂度O(n log2n)。
注意:代入主席树用root!
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<cstdio> #include<algorithm> #include<cstring> #include<cctype> using namespace std; const int maxn=100010; struct tree{int l,r,sum;}t[40000010]; struct edge{int v,from;}e[800010]; int a[maxn],root[maxn],Root[maxn],f[maxn][30],tot,cnt,first[maxn],sz,size[maxn],deep[maxn],p[10],n,m,T,b[maxn]; bool vis[maxn]; int read() { char c;int s=0,t=1; while(!isdigit(c=getchar()))if(c=='-')t=-1; do{s=s*10+c-'0';}while(isdigit(c=getchar())); return s*t; } void insert(int u,int v) {cnt++;e[cnt].v=v;e[cnt].from=first[u];first[u]=cnt;} void build(int l,int r,int &x,int y,int c){ x=++sz; t[x]=t[y];t[x].sum++; if(l==r)return; int mid=(l+r)>>1; if(c<=mid)build(l,mid,t[x].l,t[y].l,c); else build(mid+1,r,t[x].r,t[y].r,c); } void dfs(int x,int fa,int rt){ Root[x]=rt;size[x]=1;vis[x]=1; build(1,tot,root[x],root[fa],a[x]); for(int j=1;(1<<j)<=deep[x];j++)f[x][j]=f[f[x][j-1]][j-1]; for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){ f[e[i].v][0]=x; deep[e[i].v]=deep[x]+1; dfs(e[i].v,x,rt); size[x]+=size[e[i].v]; } } int lca(int x,int y){ if(deep[x]<deep[y])swap(x,y); int d=deep[x]-deep[y]; for(int j=0;(1<<j)<=d;j++)if((1<<j)&d)x=f[x][j];//&... if(x==y)return x; for(int j=20;j>=0;j--)if((1<<j)<=deep[x]&&f[x][j]!=f[y][j]){ x=f[x][j];y=f[y][j]; } return f[x][0]; } int ask(int l,int r,int c){ if(l==r)return l; int sum=t[t[p[1]].l].sum+t[t[p[2]].l].sum-t[t[p[3]].l].sum-t[t[p[4]].l].sum; int mid=(l+r)>>1; if(sum>=c){ for(int i=1;i<=4;i++)p[i]=t[p[i]].l; return ask(l,mid,c); } else{ for(int i=1;i<=4;i++)p[i]=t[p[i]].r; return ask(mid+1,r,c-sum); } } int main(){ int Test=read();//Test=1 n=read();m=read();T=read(); for(int i=1;i<=n;i++)a[i]=read(),b[i]=a[i]; sort(b+1,b+n+1);tot=n; tot=unique(b+1,b+tot+1)-b-1; for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+tot+1,a[i])-b; for(int i=1;i<=m;i++){ int u=read(),v=read(); insert(u,v);insert(v,u); } for(int i=1;i<=n;i++)if(!vis[i])dfs(i,0,i); char s[10]; int lastans=0; for(int i=1;i<=T;i++){ scanf("%s",s); if(s[0]=='Q'){ int u=read()^lastans,v=read()^lastans,w=read()^lastans; p[1]=root[u];p[2]=root[v];p[3]=root[lca(u,v)];p[4]=root[f[lca(u,v)][0]];//用root! lastans=b[ask(1,tot,w)]; printf("%d\n",lastans); } else{ int u=read()^lastans,v=read()^lastans; if(size[Root[u]]<size[Root[v]])swap(u,v); size[Root[u]]+=size[Root[v]];insert(u,v);insert(v,u); f[v][0]=u; deep[v]=deep[u]+1; dfs(v,u,Root[u]); } } return 0; }