正题
题目链接:https://www.luogu.com.cn/problem/P3302
题目大意
nnn个点的一个森林,要求支持以下操作
- 连接两个不连通的点
- 询问两个点之间最短路径上第kkk大的数
解题思路
需要支持查询第kkk大,是必定使用主席树的,所以考虑如何合并两棵树可以更快。
考虑启发式合并,每次将小的点合并到大的点上面,那样每个点最多被合并logn\log nlogn次。也就是每次合并我们就将小的那颗树完全重构
考虑如何查询,我们可以用主席树上储存根节点到该点的每个数的个数,然后查询时我们可以用两个点到根节点的答案减去他们的lcalcalca处和lcalcalca的父节点处的答案即可。
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=8e4+10,M=N*600,K=17;
struct node{ll to,next;
}a[N*2];
ll n,m,t,cnt,tot;
ll w[N],b[N],ls[N],rt[N];
ll root[N],siz[N],dep[N],f[N][K];
struct Seq_Tree{ll ls[M],rs[M],w[M],cnt;void Build(ll &x,ll l,ll r){if(!x)x=++cnt;w[x]=0;if(l==r)return;ll mid=(l+r)>>1;Build(ls[x],l,mid);Build(rs[x],mid+1,r);return;}ll Change(ll y,ll l,ll r,ll pos){ll x=++cnt,mid=(l+r)>>1;w[x]=w[y]+1;ls[x]=ls[y];rs[x]=rs[y];if(l==r)return x;if(pos<=mid)ls[x]=Change(ls[y],l,mid,pos);else rs[x]=Change(rs[y],mid+1,r,pos);return x;}ll Ask(ll x,ll y,ll lca,ll fa,ll l,ll r,ll k){if(l==r)return b[l];ll mid=(l+r)>>1,val=w[ls[x]]+w[ls[y]]-w[ls[lca]]-w[ls[fa]];if(val>=k)return Ask(ls[x],ls[y],ls[lca],ls[fa],l,mid,k);return Ask(rs[x],rs[y],rs[lca],rs[fa],mid+1,r,k-val);}
}T;
void addl(ll x,ll y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[++tot].to=x;a[tot].next=ls[y];ls[y]=tot;return;
}
void dfs(ll x,ll fa){siz[root[x]]++;f[x][0]=fa;dep[x]=dep[fa]+1;for(ll i=1;i<K;i++)f[x][i]=f[f[x][i-1]][i-1];rt[x]=T.Change(rt[fa],1,cnt,w[x]);for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(y==fa)continue;root[y]=root[x];dfs(y,x);}return;
}
void Connect(ll x,ll y){if(siz[root[x]]<siz[root[y]])swap(x,y);root[y]=root[x];dfs(y,x);addl(x,y);return;
}
ll LCA(ll x,ll y){if(dep[x]>dep[y])swap(x,y);for(ll i=K-1;i>=0;i--)if(dep[f[y][i]]>=dep[x])y=f[y][i];if(x==y)return x;for(ll i=K-1;i>=0;i--)if(f[y][i]!=f[x][i])x=f[x][i],y=f[y][i];return f[x][0];
}
int main()
{ll testcase;scanf("%lld",&testcase);scanf("%lld%lld%lld",&n,&m,&t);for(ll i=1;i<=n;i++)scanf("%lld",&w[i]),b[++cnt]=w[i];sort(b+1,b+1+cnt);cnt=unique(b+1,b+1+cnt)-b-1;for(ll i=1;i<=n;i++)w[i]=lower_bound(b+1,b+1+cnt,w[i])-b;for(ll i=1;i<=m;i++){ll x,y;scanf("%lld%lld",&x,&y);addl(x,y);}T.Build(rt[0],1,cnt);for(ll i=1;i<=n;i++)if(!root[i]){root[i]=i;dfs(i,0);}ll last=0;for(ll i=1;i<=t;i++){char op[4];ll x,y,k;scanf("%s",op);if(op[0]=='Q'){scanf("%lld%lld%lld",&x,&y,&k);x^=last;y^=last;k^=last;ll lca=LCA(x,y);last=T.Ask(rt[x],rt[y],rt[lca],rt[f[lca][0]],1,cnt,k);printf("%lld\n",last);}else{scanf("%lld%lld",&x,&y);x^=last;y^=last;Connect(x,y);}}return 0;
}