HihoCoder 1638 : 小Hi的天平 (2-sat+并查集)
描述
小Hi给小Ho邮寄了一个天平。收到天平后,小Ho想知道天平在运输过程中是否损坏,为此它准备了A类物品和B类物品共n个(可能只有A类物品,也可能只有B类物品),但无法确定一个物品是哪一类。A类物品的质量都相同,B类物品的质量也相同,但A类物品与B类物品的质量不同。现将n个物品从1到n编号,用天平进行m次测量,每次从n个物品中取i和j两个物品,测量后可以知道物品i和物品j质量是否相同。
现在小Ho想知道能否根据测量结果判定天平是坏的,如果能确定,最早是第几次测量确定的,你能帮帮他吗?
输入
第一行一个数字T,代表数据组数。1<=T<=5。
对于每组数据:
第一行两个整数n,m,分别代表A类、B类物品的总数和测量次数。2<=n<=10000,1<=m<=300000。
接下来m行,每行三个数字x,u,v。x=0代表物品u与物品v质量相等,x=1代表质量不等。
输出
对于每组数据:
若不能确定天平是坏的,则输出一行“great”。
否则,输出两行。
第一行输出“sad”。
第二行输出一个数字p,代表最早第p次测量确定了天平是坏的。
- 样例输入
1 2 2 0 1 2 1 1 2
- 样例输出
sad 2
思路:对于每一个物品i,我们可以假设i是R色,则i+n是B色,然后2-sat验证。但是这样的话二分不方便。
所以改用并查集来实现:对于每一个关系,如果相同,则合并 u和v,u+n和v+n;否则合并u和v+n,v+u+n,知道出现矛盾。
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<cstdlib> #include<string> const int maxn=20030; using namespace std; int m,n,x,u,v; int flag=1; int par[maxn],rnk[maxn]; void init( int n ) { for (int i=0;i<=n;i++){ par[i]=i; rnk[i]=0; } } int find( int x ) { return x==par[x]?x:find(par[x]); } void unite( int x, int y ) { x=find(x); y=find(y); if(x==y) return; if(rnk[x]<rnk[y]) par[x]=y; else{ par[y]=x; if (rnk[x]==rnk[y]) rnk[x] ++; } } bool same( int x, int y ) { return find(x)==find(y); } int main() { int T; scanf("%d",&T ); while (T--){ scanf("%d%d",&n,&m ); init(n*2+10); flag=1; for(int i=1;i<=m;++i){ scanf("%d%d%d",&x,&u,&v ); if (flag){ if(x==0){ if(same(u,v+n)){ puts("sad"); printf("%d\n",i); flag = 0; } else{ unite(u,v); unite(v+n,u+n); } } else{ if (same(u,v)||same(u+n,v+n)){ puts("sad"); printf( "%d\n",i); flag = 0; } else{ unite(u,v+n); unite(u+n,v); } } } } if (flag) puts("great"); } return 0; }
相关推荐
雅趣 2018-01-09