Description
一场漫长的干旱使农场主约翰的N牧场没有草。然而,随着雨季的到来,是时候“重新种植”了。在农夫约翰的小屋里,他有两个桶,每个桶都有不同类型的草籽。他想在他的每一个N牧场种草,在每一个牧场中选择一种类型的草。
作为一名奶农,农场主约翰想确保他能满足他那几头奶牛的特殊饮食需求。他的每头m奶牛都有两个最喜欢的牧场。他的一些奶牛有一个饮食限制,那就是他们应该只吃一种类型的草,因此农场主约翰希望确保在这类奶牛最喜欢的两个田里种植同一种类型的草。其他的奶牛有一个非常不同的饮食限制,要求他们吃不同类型的草。对于那些奶牛,农场主约翰当然想确保他们最喜欢的两块田地里有不同的草。
请帮助农场主约翰确定他在他的N牧场上种植草的不同方式的数量。
Input
输入的第一行包含N(2≤n≤105)和m(1≤m≤105)。
下一行m中的每一行都包含一个“s”或“d”字符,后跟两个1…n范围内的整数,描述农场主约翰的一头牛最喜欢的两个牧场。如果字符是“s”,则这一行表示一头母牛在其两个最喜爱的牧场中需要相同类型的草。如果字符为“d”,则该行表示需要不同草类型的奶牛。
Output
输出农场主约翰在他的N牧场上植草的方式。请用二进制写你的答案。
Sample Input
3 2
S 1 2
D 3 2
Sample Output
10
思路
- 种类并查集+联通快
- 观察题目发现只要有要求的牧场冲突,答案为0;不冲突则答案为2的cnt次方(cnt为联通块数目)
- 三个并查集,一个存牧场是否被提到,另外两个种类并查集判断他们有没有冲突
- 第一次写种类并查集,核心思想:朋友的朋友是朋友,朋友的敌人是敌人,敌人的敌人是朋友
代码
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=1000005;
int n,m,ans,fa[3*maxn];
int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
void join(int x,int y){int fx=getfa(x),fy=getfa(y); if(fx!=fy) fa[fy]=fx;}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=3*n;++i) fa[i]=i;
for(int i=1;i<=m;++i)
{
char a[3]; int x,y; scanf("%s%d%d",&a,&x,&y);
join(x,y);
if(a[0]=='S') {
if(fa[x+n]==fa[y+2*n]||fa[x+2*n]==fa[y+n]){puts("0"); return 0;}
join(x+n,y+n);
join(x+2*n,y+2*n);
}
else {
if(fa[x+n]==fa[y+n]){puts("0"); return 0;}
join(x+2*n,y+n);
join(x+n,y+2*n);
}
}
for(int i=1;i<=n;++i) if(fa[i]==i) ++ans;
cout<<'1'; while(ans--) cout<<'0';
return 0;
}