给定一个 n 个点 m 条边的无向无权图。
点的编号为 1∼n。
图中不含重边和自环。
现在,请你给图中的每个点进行赋值,要求:
- 每个点的权值只能是 1 或 2 或 3。
- 对于图中的每一条边,其两端点的权值之和都必须是奇数。
请问,共有多少种不同的赋值方法。
由于结果可能很大,你只需要输出对 998244353 取模后的结果。
输入格式
第一行包含整数 T,表示共有 T 组测试数据。
每组数据第一行包含两个整数 n,m。
接下来 m 行,每行包含两个整数 u,v,表示点 u 和点 v 之间存在一条边。
输出格式
一个整数,表示不同赋值方法的数量对 998244353 取模后的结果。
数据范围
前三个测试点满足 1≤T≤6,∑i=1Tm≤50。
所有测试点满足 1≤T≤3×105,1≤n≤3×105,0≤m≤3×105,1≤u,v≤n,∑i=1Tn≤3×105,∑i=1Tm≤3×105。
输入样例:
2
2 1
1 2
4 6
1 2
1 3
1 4
2 3
2 4
3 4
输出样例:
4
0
分析:
首先可以知道显然合法方案是 : (奇数−偶数−奇数−偶数)
因此考虑二分图染色法进行黑白染色 , 记录 黑色的数量cnt1,白色的数量cnt2,对于奇数有两种选择 , 而奇数既可以放黑色也可以放白色,因此对于一个连通块内的答案就是2的cnt1次方+2的cnt2次方,需要注意的是一旦一个连通块不合法则整体为0,所有连通块方案数相乘为答案。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int mod=998244353;
const int N=3e5+10;
vector<int>adj[N];
long long ans;
int st[N];
int t,n,m,flag,cnt1,cnt2;
bool dfs(int u,int an)
{
//cout<<"u: "<<u<<endl;
st[u]=an;
int w=3-an;
if(an==1) cnt1++;
else cnt2++;
for(int i=0;i<adj[u].size();i++)
{
int v=adj[u][i];
if(st[v])
{
if(st[v]!=w)return false;
}
else
{
if(!dfs(v,w))return false;
}
}
return true;
}
long long func(int x)
{
long long res=1,la=2;
while(x)
{
if(x%2)res=(res*la)%mod;
la=(la*la)%mod;
x/=2;
}
return res;
}
int main()
{
cin>>t;
while(t--)
{
cin>>n>>m;
while(m--)
{
int u,v;
cin>>u>>v;
adj[u].push_back(v);
adj[v].push_back(u);
}
ans=1;
flag=0;
for(int i=1;i<=n;i++)
{
if(st[i])continue;
cnt1=cnt2=0;
if(!dfs(i,1))
{
flag=1;
break;
}
//cout<<cnt1<<" "<<cnt2<<endl;
ans=(ans*(func(cnt1)+func(cnt2)))%mod;
}
if(flag)ans=0;
cout<<ans<<endl;
ans=0;
fill(st,st+n+1,0);
for(int i=1;i<=n;i++)adj[i].clear();
}
return 0;
}