Gym - 100502A Amanda Lounges (二分图染色)
题目大意:n个点和m条限制(n,m<=4e5),每条限制可以规定两个点:
1)都是白色2)都是黑色3)一白一黑
问最多有多少个黑点,如果不能满足所有限制条件则输出impossible。
这是去年在训练赛上打过的一道题,因为比较经典所以决定fuxi一下。当时我很早就写出来了,然而wa了很多发最后才过的,坑点太多。
总体思路就是对于1,2类限制,直接给两点染上对应的颜色,对于3类限制,由于无法直接确定两点的颜色,因此给两点连边,有边直接相连的两点不能为同一颜色,这样就转化成了经典的二分图染色问题。
具体步骤大致如下:
1)构建限制,构建的过程中同时检查是否有矛盾(比如一个点已经被染成了白色,后来又来了一条边让它成为黑色,这样是不行的)。
2)对每一个已经染色的点,用dfs给它所在的整个连通块染色,染色的过程中检查是否有矛盾。(这一步将所有能确定颜色的点都确定下来了)。
3)剩下的不能确定颜色的点一定能够构成若干个连通块,对于每个连通块任选一点将其染成任意颜色,分别求出每个连通块黑色和白色的数量。
1 #include<bits/stdc++.h> 2 typedef long long ll; 3 const int N=2e5+10,inf=0x3f3f3f3f; 4 using namespace std; 5 int hd[N],ne,n,m,col[N],flag,black,white; 6 struct E {int v,nxt;} e[N<<1]; 7 void link(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;} 8 int dfs(int u,int c) { 9 if(~col[u])return col[u]==c; 10 col[u]=c; 11 c==1?++black:++white; 12 for(int i=hd[u]; ~i; i=e[i].nxt) { 13 int v=e[i].v; 14 if(!dfs(v,c^1))return 0; 15 } 16 return 1; 17 } 18 int solve() { 19 for(int u=1; u<=n; ++u)if(~col[u]) 20 for(int i=hd[u]; ~i; i=e[i].nxt) { 21 int v=e[i].v; 22 if(!dfs(v,col[u]^1))return -1; 23 } 24 int ret=0; 25 for(int i=1; i<=n; ++i)if(col[i]==1)++ret; 26 for(int i=1; i<=n; ++i)if(!~col[i]) { 27 black=white=0; 28 if(!dfs(i,0))return -1; 29 ret+=min(black,white); 30 } 31 return ret; 32 } 33 int main() { 34 scanf("%d%d",&n,&m); 35 memset(hd,-1,sizeof hd),ne=0; 36 memset(col,-1,sizeof col); 37 flag=1; 38 while(m--) { 39 int u,v,c; 40 scanf("%d%d%d",&u,&v,&c); 41 if(c==0) { 42 if(col[u]==1||col[v]==1)flag=0; 43 col[u]=col[v]=0; 44 } else if(c==2) { 45 if(col[u]==0||col[v]==0)flag=0; 46 col[u]=col[v]=1; 47 } else link(u,v),link(v,u); 48 } 49 if(!flag)puts("impossible"); 50 else { 51 int ans=solve(); 52 if(~ans)printf("%d\n",ans); 53 else puts("impossible"); 54 } 55 return 0; 56 }