[SDOI2013] 森林(主席树+启发式合并)
作者:互联网
description
给一个森林,每个点有权值。在线(每次每个变量xor lastans)
操作如下:
- Q x y k:查询x到y的路径上第k小的权值(保证x到y至少k个点)
- L x y:将x和y连边。
solution
从Q和L分别可以猜测需要用到主席树和lct。
主席树是我一直不太熟的数据结构,作用可以维护一段历史版本的方案(每一个历史版本都是上一个版本加简单增量而来的),查询一段版本则是差分。
一般线性表,就从左往右依次加入(利用上一个版本加上增量,增量一般新建log级别的节点)
首先不会虽然还可做的lct……
询问:可以想象为主席树,每个节点维护到根的各个权值的个数。每个节点可以由父亲版本推到,因此insert操作也是很好写的。
还需要倍增求lca。路径某权值个数为:两端到根的和-lca到根-lca的父亲到根。
操作:加边等同于合并两个连通块。用并查集维护连通块以及大小。然后发现主席树这种东西不好修改,一次只能对一个节点的版本改。多个节点,不就是暴力了吗?
优化暴力,用启发式合并即可。时空复杂度\(O(nlog^2n)\)
具体就dfs轻的连通块里每一个点,暴力更新(父亲,dep,倍增数组,对应主席树版本)。
*ps.70pt的注意:dfs中更新节点的倍增数组时要更新到\(2^i>=n\)而不是\(2^i>=dep\),因为是动态图,之前的\(dep\)可能比现在大的,所以没更新到的本来应该默认为\(0\),但此时可能非\(0\),要再更一下(相当于清空)。这题直接更到\(16\)就行了。
code
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
const int M=1e7+5;
int fa[N],sz[N],nd,val[N],lg[N],f[N][21],dep[N];
int g_fa(int u) {return fa[u]==u?u:fa[u]=g_fa(fa[u]);}
struct seg {int cnt,ls,rs;}T[M];
int rt[N],n,m,nxt[N],to[N],head[N],ecnt,a[N];
void add_edge(int u,int v) {
nxt[++ecnt]=head[u];to[ecnt]=v;head[u]=ecnt;
nxt[++ecnt]=head[v];to[ecnt]=u;head[v]=ecnt;
}
int LCA(int x,int y) {
if(dep[x]<dep[y]) {swap(x,y);}
int k=dep[x]-dep[y];for(int i=0;i<=lg[k];i++)if((1<<i)&k)x=f[x][i];
if(x==y)return x;
for(int i=lg[dep[x]];i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
return f[x][0];
}
void Build(int &x,int l,int r) { //Build(rt[0])用这样每个节点都能指向它有完整的左右子树信息
x=++nd;
if(l==r)return;
int mid=(l+r)>>1;
Build(T[x].ls,l,mid),Build(T[x].rs,mid+1,r);
}
void Insert(int &x,int fx,int l,int r,int v) {
x=++nd;
T[x]=T[fx]; T[x].cnt++;
if(l==r)return;
int mid=(l+r)>>1;
(v<=mid)?Insert(T[x].ls,T[fx].ls,l,mid,v):Insert(T[x].rs,T[fx].rs,mid+1,r,v);
}
void dfs(int x,int fx) {
dep[x]=dep[fx]+1;
f[x][0]=fx;
for(int i=1;i<=16;i++) {f[x][i]=f[f[x][i-1]][i-1];} //必须更到 16,有一些虽然到不了,但要赋0(clear\kk)
Insert(rt[x],rt[fx],1,m,a[x]);
for(int i=head[x];i;i=nxt[i]) {
int y=to[i];if(y==fx)continue;
dfs(y,x);
}
}
int lans;
void Query(int x,int y,int fx,int fy,int l,int r,int k) { //x,y,lca,fa[lca]
if(l==r) {lans=val[l];return;}
int w=T[T[x].ls].cnt+T[T[y].ls].cnt-T[T[fx].ls].cnt-T[T[fy].ls].cnt,mid=(l+r)>>1;
if(w>=k) {Query(T[x].ls,T[y].ls,T[fx].ls,T[fy].ls,l,mid,k);}
else {Query(T[x].rs,T[y].rs,T[fx].rs,T[fy].rs,mid+1,r,k-w);}
}
int main() {
// freopen("P3302_6.in","r",stdin);
// freopen("ans.out","w",stdout);
int testcase;scanf("%d",&testcase);
int cm,T;scanf("%d%d%d",&n,&cm,&T);
lg[1]=0;for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;i++) {scanf("%d",&a[i]);val[i]=a[i];}
sort(val+1,val+1+n);m=unique(val+1,val+1+n)-val-1;
for(int i=1;i<=n;i++) {sz[i]=1;fa[i]=i;a[i]=lower_bound(val+1,val+1+m,a[i])-val;}
for(int i=1;i<=cm;i++) {
int x,y;scanf("%d%d",&x,&y);add_edge(x,y);
int u=g_fa(x),v=g_fa(y);
if(u==v)continue;
sz[v]+=sz[u];fa[u]=v;
}
Build(rt[0],1,m);
for(int i=1;i<=n;i++) {
if(g_fa(i)==i) {dfs(i,0);}
}
for(int i=1;i<=T;i++) {
char ch[3];int x,y,k;
scanf("%s%d%d",ch,&x,&y);
x^=lans,y^=lans;
if(ch[0]=='Q') {
scanf("%d",&k);k^=lans;
int c=LCA(x,y);
Query(rt[x],rt[y],rt[c],rt[f[c][0]],1,m,k);
printf("%d\n",lans);
}
else {
add_edge(x,y);
int u=g_fa(x),v=g_fa(y);
if(sz[u]>sz[v]){swap(x,y);swap(u,v);}
fa[u]=v;sz[v]+=sz[u];
dfs(x,y);
}
}
return 0;
}
标签:rs,int,合并,mid,ecnt,fa,SDOI2013,ls,启发式 来源: https://www.cnblogs.com/bestime/p/16473340.html