[BZOJ4668]冷战(并查集)

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

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

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

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

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

$O(n\log n)$

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 4 using namespace std;
 5 
 6 const int N=1000010;
 7 int n,m,op,u,v,ans,tim,he[N],fa[N],pre[N];
 8 
 9 int find(int x){ return (fa[x]==x) ? x : find(fa[x]); }
10 
11 void uni(int u,int v,int tim){
12     u=find(u); v=find(v);
13     if (u==v) return;
14     if (he[u]<he[v]) swap(u,v);
15     fa[v]=u; pre[v]=tim;
16     if (he[u]==he[v]) he[u]++;
17 }
18 
19 int lca(int u,int v){
20     if (find(u)!=find(v)) return 0;
21     int s1=0,s2=0,x=u,y=v,res=0;
22     while (x!=fa[x]) s1++,x=fa[x];
23     while (y!=fa[y]) s2++,y=fa[y];
24     while (s1>s2) res=max(res,pre[u]),u=fa[u],s1--;
25     while (s2>s1) res=max(res,pre[v]),v=fa[v],s2--;
26     while (u!=v) res=max(res,max(pre[u],pre[v])),u=fa[u],v=fa[v];
27     return res;
28 }
29 
30 int main(){
31     freopen("bzoj4668.in","r",stdin);
32     freopen("bzoj4668.out","w",stdout);
33     scanf("%d%d",&n,&m);
34     rep(i,1,n) fa[i]=i,he[i]=1;
35     rep(i,1,m){
36         scanf("%d%d%d",&op,&u,&v); u^=ans; v^=ans;
37         if (op==0) uni(u,v,++tim); else printf("%d\n",ans=lca(u,v));
38     }
39     return 0;
40 }

 

posted @ 2018-12-10 19:42  HocRiser  阅读(166)  评论(0编辑  收藏  举报