[BZOJ4668]冷战(并查集)

比较自然的思路是,由于需要记录连通块合并时的信息,所以需要建出Kruskal重构树。

需要用LCT维护,支持加点和在线LCA操作。

不妨考虑在并查集合并的同时记录信息,pre[x]表示x与它的父亲相连的时刻。

两个点连通的时刻,等于两个点之间路径上时刻的最大值。

注意到按秩合并但不路径压缩的并查集不改变树的结构,且树高为log,正好符合要求。

$O(n\log n)$

 #include<cstdio>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
using namespace std; const int N=;
int n,m,op,u,v,ans,tim,he[N],fa[N],pre[N]; int find(int x){ return (fa[x]==x) ? x : find(fa[x]); } void uni(int u,int v,int tim){
u=find(u); v=find(v);
if (u==v) return;
if (he[u]<he[v]) swap(u,v);
fa[v]=u; pre[v]=tim;
if (he[u]==he[v]) he[u]++;
} int lca(int u,int v){
if (find(u)!=find(v)) return ;
int s1=,s2=,x=u,y=v,res=;
while (x!=fa[x]) s1++,x=fa[x];
while (y!=fa[y]) s2++,y=fa[y];
while (s1>s2) res=max(res,pre[u]),u=fa[u],s1--;
while (s2>s1) res=max(res,pre[v]),v=fa[v],s2--;
while (u!=v) res=max(res,max(pre[u],pre[v])),u=fa[u],v=fa[v];
return res;
} int main(){
freopen("bzoj4668.in","r",stdin);
freopen("bzoj4668.out","w",stdout);
scanf("%d%d",&n,&m);
rep(i,,n) fa[i]=i,he[i]=;
rep(i,,m){
scanf("%d%d%d",&op,&u,&v); u^=ans; v^=ans;
if (op==) uni(u,v,++tim); else printf("%d\n",ans=lca(u,v));
}
return ;
}
上一篇:群论第五章转动群(2)老师说伴随表示要求学会


下一篇:bzoj 4668 冷战——并查集结构