BZOJ 3123 森林 主席树启发式合并
Description
Input
第一行包含一个正整数testcase,表示当前测试数据的测试点编号。保证1≤testcase≤20。
第二行包含三个整数N,M,T,分别表示节点数、初始边数、操作数。第三行包含N个非负整数表示 N个节点上的权值。
接下来 M行,每行包含两个整数x和 y,表示初始的时候,点x和点y 之间有一条无向边, 接下来 T行,每行描述一个操作,格式为“Q x y k”或者“L x y ”,其含义见题目描述部分。
Output
对于每一个第一类操作,输出一个非负整数表示答案。
Sample Input
1
8 4 8
1 1 2 2 3 3 4 4
4 7
1 8
2 4
2 1
Q 8 7 3 Q 3 5 1
Q 10 0 0
L 5 4
L 3 2 L 0 7
Q 9 2 5 Q 6 1 6
8 4 8
1 1 2 2 3 3 4 4
4 7
1 8
2 4
2 1
Q 8 7 3 Q 3 5 1
Q 10 0 0
L 5 4
L 3 2 L 0 7
Q 9 2 5 Q 6 1 6
Sample Output
2
2
1
4
2
如果这道题不带添加边的操作的话直接上主席树就可以了,那么现在加了边,主席树上树的朴素做法已经行不通了,那么考虑一下加了边实际上就是把两个联通分量连成了一个联通分量,那么我们就需要对两个联通分量所代表的主席树进行合并,那么为了保证时间复杂度,我们每次把size较小的联通分量合并到size较大的联通分量中去,并且把对应的主席树进行合并。然后就是朴素的主席树上树了。
2
1
4
2
#include <stdio.h> #include <cstring> #include <algorithm> const int MAXN = 100005; int first[MAXN],e=1,cnt,n,m,q,val[MAXN],id[MAXN],num,Hash[MAXN],sz; int belong[MAXN],fa[MAXN][25],size[MAXN],root[MAXN],Ans,deep[MAXN]; bool vis[MAXN]; template<typename _t> inline _t read(){ _t x=0,f=1; char ch=getchar(); for(;ch>'9'||ch<'0';ch=getchar())if(ch=='-')f=-f; for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+(ch^48); return x*f; } struct node{ int l,r,sum; }tree[20000005]; #define ls(x) tree[x].l #define rs(x) tree[x].r #define sum(x) tree[x].sum struct edge{ int u,v,next; }a[MAXN<<3]; void push(int u,int v){ a[e].u=u;a[e].v=v; a[e].next=first[u]; first[u]=e++; } void insert(int &o,int old,int l,int r,int x){ o=++cnt; tree[o]=tree[old]; sum(o)++; if(l==r)return; int m=l+r>>1; if(x<=m)insert(ls(o),ls(old),l,m,x); else insert(rs(o),rs(old),m+1,r,x); return; } inline void swap(int &x,int &y){x^=y;y^=x;x^=y;} inline void Hash_init(){std :: sort(&Hash[1],&Hash[n+1]);sz = std :: unique(&Hash[1],&Hash[n+1])-Hash-1;} inline int GHash(int x){return std :: lower_bound(&Hash[1],&Hash[sz+1],x)-Hash;} void build(int &o,int l,int r){ o=++cnt; sum(o)=0; if(l==r)return; int m=l+r>>1; build(ls(o),l,m); build(rs(o),m+1,r); return; } void dfs(int u,int f,int be){ size[u]=1; vis[u]=1;deep[u]=deep[f]+1; fa[u][0]=f;belong[u]=be; insert(root[u],root[f],1,sz,GHash(val[u])); for(int i=1;i<=19;i++) fa[u][i]=fa[fa[u][i-1]][i-1]; for(int i=first[u];i;i=a[i].next){ if(a[i].v==f)continue; dfs(a[i].v,u,be); size[u]+=size[a[i].v]; } return; } inline int lca(int x,int y){ if(deep[x]<deep[y])swap(x,y); int t=deep[x]-deep[y]; for(int i=0;i<=19;i++) if(t&(1<<i)) x=fa[x][i]; if(x==y)return x; for(int i=19;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; return fa[x][0]; } int Query(int u,int v,int k){ int x = lca(u,v),y=fa[x][0]; int l=1,r=sz; int root_u=root[u],root_v=root[v],root_lca=root[x],root_fa=root[y]; while(l<r){ int tmp = tree[tree[root_u].l].sum-tree[tree[root_fa].l].sum+tree[tree[root_v].l].sum-tree[tree[root_lca].l].sum; int m=l+r>>1; if(tmp>=k){ root_u=tree[root_u].l;root_lca=tree[root_lca].l; root_v=tree[root_v].l;root_fa =tree[root_fa].l; r=m; } else{ k-=tmp; root_u=tree[root_u].r;root_lca=tree[root_lca].r; root_v=tree[root_v].r;root_fa =tree[root_fa].r; l=m+1; } } return Hash[l]; } int main(){ read<int>(); n=read<int>();m=read<int>();q=read<int>(); for(int i=1;i<=n;i++)val[i]=read<int>(),Hash[i]=val[i]; for(int i=1;i<=m;i++){ int u=read<int>(),v=read<int>(); push(u,v);push(v,u); }Hash_init(); build(root[0],1,sz); for(int i=1;i<=n;i++)if(!vis[i])dfs(i,0,++num),id[num]=i; while(q--){ char ch=getchar(); while(ch!='Q'&&ch!='L')ch=getchar(); if(ch=='Q'){ int x=read<int>()^Ans,y=read<int>()^Ans,k=read<int>()^Ans; printf("%d\n",Ans=Query(x,y,k)); } else{ int x=read<int>()^Ans,y=read<int>()^Ans; if(size[id[belong[x]]]>size[id[belong[y]]])swap(x,y); push(y,x);push(x,y);size[id[belong[y]]]+=size[id[belong[x]]]; dfs(x,y,belong[y]); } } }