蒟蒻第一次通过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
",ans%mod);
}
return 0;
}
Tags:,二分图,图论,快速幂