1.模板题

洛谷3367

我们把这个问题换一种方式表述

1 u v 表示v是u的祖先

2 u v 问你u和v是不是同一家族

此处设一些变量

anc[i]表示i的祖先,一开始anc[i]=0。

 

然后对于输入的信息1 u v

我们找到u和v所已知最早的祖先fu,fv;

while(!anc[u]) u=anc[u];

类似于此

此时如果fu==fv说明之前已经有信息把u,v连接在了一起,则不做处理;

如果fu!=fv,那么另anc[fu]=fv;

(注意此题我们可能无法确定fu与fv谁是最早祖先,但是不影响正确性)

 

对于输入的询问2 u v

我们也是找到 fu   fv

看fu fv 是否相等就可以了

 

然而我们仔细思考这个过程发现在极端情况下,耗时会十分的长

例如给定信息表示1与2,2与3,3与4,4与5........n与n-1的关系

那么这些元素将会连成一条链

我们每次查询1 的最早祖先, 都要找n次

这样就会使时间复杂度过高

 

思考我们怎么优化这个过程呢?

 

路径压缩:

首先对于一个家族里面所有的人,我不关心别的,只关心这个家族最早的祖先

那么我们是不是可以考虑把每个点都直接连接在哪个最早祖先上。

下面给出代码:

 1 #include<cstdio>
 2 const int N=10005;
 3 int anc[N];
 4 int fi(int a){
 5     int b=a;
 6     while(anc[b]) b=anc[b];
 7     while(anc[a]){
 8         int t=anc[a];
 9         anc[a]=b;
10         a=t;
11     }
12     return b;
13 }
14 int main(){
15     int n,m;
16     scanf("%d%d",&n,&m);
17     while(m--){
18         int id,u,v;
19         scanf("%d%d%d",&id,&u,&v);
20         int fu=fi(u),fv=fi(v);
21         if(id==2){
22             if(fu==fv) printf("Y\n");
23             else printf("N\n");
24         }
25         else if(fu!=fv) anc[fu]=fv;
26     }
27     return 0;
28 } 

一些加权并查集的题解:

luogu2024

bzoj1202