Luogu_P3302 [SDOI2013]森林
### 主席树,启发式合并,lca
luogu题面
求树上路径的第k大,树之间还有合并。
明显是主席树再加合并。
先说链上第k大,其实就是\(Tx+Ty-Tlca-Tlcafa\)
\(T\)表示权值线段树。
主席树维护的是从根节点到当前节点的前缀和。
ask的代码如下:
inline int ask(int x,int y,int lcc,int lcf,int l,int r,int k){
if(l==r) return b[l];
int lz=sum(lc(x))+sum(lc(y))-sum(lc(lcc))-sum(lc(lcf));
int mid=(l+r)>>1;
if(k<=lz) return ask(lc(x),lc(y),lc(lcc),lc(lcf),l,mid,k);
else return ask(rc(x),rc(y),rc(lcc),rc(lcf),mid+1,r,k-lz);
}
然后就是合并了。
合并就是启发式合并。小的向大的合并。
在dfs的过程中合并,更新线段树。
dfs代码:
void dfs(int x,int ft,int rrt){
fa[x][0]=ft;
for(int i=1;i<=tt;i++)
fa[x][i]=fa[fa[x][i-1]][i-1];
dep[x]=dep[ft]+1;vis[x]=1;f[x]=ft;siz[rrt]++;
int k=lower_bound(b+1,b+1+cnt,a[x])-b;
rt[x]=insert(rt[x],rt[ft],1,cnt,k);
for(int i=head[x];i;i=nxt(i)){
if(to(i)==ft) continue;
dfs(to(i),x,rrt);
}
}
那么整道题就几乎解决了,还有一些细节自己注意。
代码如下:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+10;
int fe,n,m,q,tt,a[maxn],b[maxn],head[maxn],tot,fa[maxn][32],ans,cnt;
int dep[maxn],rt[maxn],vis[maxn],f[maxn],zho,siz[maxn];
struct node{
int nxt,to;
#define nxt(x) e[x].nxt
#define to(x) e[x].to
}e[maxn*4];
struct tree{
int lc,rc,sum;
#define sum(x) t[x].sum
#define lc(x) t[x].lc
#define rc(x) t[x].rc
}t[maxn*600];
inline void add(int from,int to){to(++tot)=to;nxt(tot)=head[from];head[from]=tot;}
inline int find(int x){return f[x]==x ? f[x] : f[x]=find(f[x]) ;}
inline int insert(int p,int pr,int l,int r,int k){
if(!p) p=++zho;
sum(p)=sum(pr)+1;
if(l==r) return p;
int mid=(l+r)>>1;
if(k<=mid) lc(p)=insert(lc(p),lc(pr),l,mid,k),rc(p)=rc(pr);
else rc(p)=insert(rc(p),rc(pr),mid+1,r,k),lc(p)=lc(pr);
return p;
}
inline int ask(int x,int y,int lcc,int lcf,int l,int r,int k){
if(l==r) return b[l];
int lz=sum(lc(x))+sum(lc(y))-sum(lc(lcc))-sum(lc(lcf));
int mid=(l+r)>>1;
if(k<=lz) return ask(lc(x),lc(y),lc(lcc),lc(lcf),l,mid,k);
else return ask(rc(x),rc(y),rc(lcc),rc(lcf),mid+1,r,k-lz);
}
void dfs(int x,int ft,int rrt){
fa[x][0]=ft;
for(int i=1;i<=tt;i++)
fa[x][i]=fa[fa[x][i-1]][i-1];
dep[x]=dep[ft]+1;vis[x]=1;f[x]=ft;siz[rrt]++;
int k=lower_bound(b+1,b+1+cnt,a[x])-b;
rt[x]=insert(rt[x],rt[ft],1,cnt,k);
for(int i=head[x];i;i=nxt(i)){
if(to(i)==ft) continue;
dfs(to(i),x,rrt);
}
}
inline int lca(int x,int y){
if(dep[y]>dep[x]) swap(x,y);
for(int i=tt;i>=0;i--)
if(dep[fa[x][i]]>=dep[y])
x=fa[x][i];
if(x==y) return x;
for(int i=tt;i>=0;i--)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
signed main(){
scanf("%lld%lld%lld%lld",&fe,&n,&m,&q);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]),b[i]=a[i];
sort(b+1,b+1+n);
cnt=unique(b+1,b+1+n)-b-1;
for(int u,v,i=1;i<=m;i++) scanf("%lld%lld",&u,&v),add(u,v),add(v,u);
tt=(int)log2(n)+1;
for(int i=1;i<=n;i++) if(!vis[i]) dfs(i,0,i),f[i]=i;
while(q--){
char s[10];int x,y;scanf("%s%lld%lld",s,&x,&y);
if(s[0]=='Q'){
int z;scanf("%lld",&z);x^=ans,y^=ans,z^=ans;
int lc=lca(x,y);ans=ask(rt[x],rt[y],rt[lc],rt[fa[lc][0]],1,cnt,z);
printf("%lld\n",ans);
}
else{
x^=ans,y^=ans;add(x,y);add(y,x);
int xx=find(x),yy=find(y);
if(siz[xx]<siz[yy]) dfs(x,y,yy);
else dfs(y,x,xx);
}
}
return 0;
}