Codeforces 1093D Beautiful Graph|二分图染色
蒟蒻第一次通过CF D题~QAQ
言归正传。本题的大意是给每个点赋上$1$、$2$ 、$3$这三种点权之一,使得每条边所连的两个点的点权之和为奇数,问有多少种赋值方案。
首先,我们知道“奇数+偶数=奇数”。显然,凡是能赋值为1的点一定能赋值为3。所以其实我们对于每个点,只有赋值为奇数或偶数两种选择。一旦一条边所连的一个点被赋值为奇数,那么另外一个点就要被赋值为偶数。(有没有发现很像二分图?)
那么,对于每个连通块,我们可以类似用二分图判定的染色法:选定一个起点先行染色,然后用搜索对每个点进行染色。如果染色出现了冲突,整张图的方案数就为$0$。
设此连通块中有$n$个点,$c1$个点被赋值成了奇数,根据乘法原理,这个连通块中赋值方案为$2^{c1}+2^{n-c1}$。($2^{n-c1}$是因为将每个点取反依然可行)同样根据乘法原理,整张图的染色方案是每个联通块的方案数相乘之积。
对于求$2^x$,我们可以用快速幂去求。
上代码
#include<bits/stdc++.h> using namespace std; const long long mod=998244353; int cc,to[900000],net[900000],fr[900000],q[900000],color[900000],ttt,n,m,u,v,h,t; long long ans,cnt1; bool vis[900000]; void addedge(int u,int v) { cc++; to[cc]=v;net[cc]=fr[u];fr[u]=cc; } long long p(int x) { //快速幂求2^x long long ans=1,kkk=2; while (x) { if (x&1) { ans*=kkk; ans%=mod; } kkk*=kkk; kkk%=mod; x>>=1; } return ans; } int main() { scanf("%d",&ttt); for (int tt=1;tt<=ttt;tt++) { scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) { scanf("%d%d",&u,&v); addedge(u,v); addedge(v,u); } int bb=1;ans=1; while (1) { h=1;t=1;q[1]=bb;vis[bb]=1;color[bb]=1;cnt1=1; while (h<=t) { //BFS染色 for (int i=fr[q[h]];i;i=net[i]) { int y=to[i]; if (!vis[y]) { color[y]=3-color[q[h]]; if (color[y]==1) cnt1++; q[++t]=to[i]; vis[y]=1; } else { if (color[y]!=3-color[q[h]]) ans=0; } } h++; } ans*=(p(cnt1)%mod+max(p(t-cnt1),(long long)(1))%mod)%mod; ans%=mod; for (int i=bb+1;i<=n;i++) { if (!vis[i]) {bb=i;break;}//找未被遍历的连通块 } if (bb==q[1]) break; } for (int i=1;i<=n;i++) fr[i]=0,vis[i]=false,color[i]=0; cc=0; printf("%d\n",ans%mod); } return 0; }
Tags:,二分图,图论,快速幂