并查集&&带权并查集BZOJ3296&&POJ1182
Posted Yiyi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并查集&&带权并查集BZOJ3296&&POJ1182相关的知识,希望对你有一定的参考价值。
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的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ4025 二分图 分治 并查集 二分图 并查集按秩合并 带权并查集