码迷,mamicode.com
首页 > 其他好文 > 详细

【并查集&&带权并查集】BZOJ3296&&POJ1182

时间:2016-11-15 00:41:18      阅读:193      评论:0      收藏:0      [点我收藏+]

标签:多少   带权并查集   find   输入   class   size   bit   i++   math   

bzoj1529[POI2005]ska Piggy banks

【题目大意】

n头奶牛m种语言,每种奶牛分别掌握一些语言。问至少再让奶牛多学多少种语言,才能使得它们能够直接或间接交流?

【思路】

(n+m)个点,奶牛学会某种语言就合并它和语言的节点。并查集维护联通块,答案为联通块个数-1。水,可是我跳坑了。

我一开始做法是设总的联通块有(n+m)个,然后没合并一次减去1。其实这样是不可行的,因为我们只需要考虑奶牛(即节点1..n)有几个联通块。有可能一些语言根本没有任何奶牛掌握……

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int MAXN=10000+30000+50;
 4 int n,m,t;
 5 int u[MAXN],h[MAXN],appear[MAXN];
 6 
 7 int find(int x)
 8 {
 9     int tmp=x;
10     while (u[tmp]!=tmp) tmp=u[tmp];
11     while (u[x]!=x)
12     {
13         int temp=u[x];
14         u[x]=tmp;
15         x=temp;
16     }
17     return tmp;
18 }
19 
20 void union_set(int fa,int fb)
21 {
22     if (h[fa]>=h[fb])
23     {
24         u[fb]=fa;
25         if (h[fa]==h[fb]) h[fa]++;
26     }
27     else u[fa]=fb;
28 }
29 
30 void solve()
31 {
32     scanf("%d%d",&n,&m);
33     for (int i=1;i<=(n+m);i++) u[i]=i,h[i]=0;
34     for (int i=1;i<=n;i++)
35     {
36         int k,lang;
37         scanf("%d",&k);
38         for (int j=1;j<=k;j++)
39         {
40             scanf("%d",&lang);
41             int fa=find(i),fb=find(lang+n);
42             if (fa!=fb)    union_set(fa,fb);
43         }
44     }
45     int t=0;
46     memset(appear,0,sizeof(appear));
47     for (int i=1;i<=n;i++)
48     {
49         int fa=find(i);
50         if (appear[fa]==0) t++,appear[fa]=1;
51     }
52     printf("%d",t-1);
53 }
54 
55 int main()
56 {
57     solve();
58     return 0;
59 }

POJ1182-[NOI01]食物链

【题目大意】

动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是"1 X Y",表示X和Y是同类。
第二种说法是"2 X Y",表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。

【思路】

*我上一次写这道题是在一年半之前。哦。

用0表示当前节点和u[i]同类,1表示当前节点i吃u[i],2表示u[i]吃当前节点i。

我们设一个v,对于题目中的输入,当为1时,v=0,当为2时,v=1。

显然如果要三个节点a、b、c,其中u[b]=c,u[a]=b,如果要压缩为u[a]=c,那么rank[a]=(rank[a]+rank[b])%3。

然后其他推一推,详见程序吧..

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 const int MAXN=50000+50;
 8 int rank[MAXN],u[MAXN];
 9 int n,k,ans;
10 
11 int find(int x)
12 {
13     if (x!=u[x])
14     {
15         int tmp=u[x];
16         u[x]=find(u[x]);
17         rank[x]=(rank[x]+rank[tmp])%3;
18     } 
19     return u[x];
20 } 
21 
22 void init()
23 {
24     ans=0;
25     scanf("%d%d",&n,&k);
26     for (int i=1;i<=n;i++) rank[i]=0,u[i]=i;
27 }
28 
29 void solve()
30 {
31     for (int i=1;i<=k;i++)
32     {
33         int d,x,y;
34         scanf("%d%d%d",&d,&x,&y);
35         if (x>n||y>n||((d==2) && (x==y))) 
36         {
37             ans++;
38             continue;
39         }
40         int fx=find(x),fy=find(y);
41         int v=(d==1)?0:1;
42         if (fx!=fy)
43         {
44             u[fx]=fy;
45             rank[fx]=(rank[y]+v-rank[x]+3)%3;
46         }
47         else
48             if (rank[x]!=(rank[y]+v)%3) ans++;
49     }
50     printf("%d",ans);
51 }
52 
53 int main()
54 {
55     init();
56     solve();
57     return 0;
58 } 

 

【并查集&&带权并查集】BZOJ3296&&POJ1182

标签:多少   带权并查集   find   输入   class   size   bit   i++   math   

原文地址:http://www.cnblogs.com/iiyiyi/p/6063896.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!