题意:
n个点m条边组成的无向图,边没有权值。现在想给点赋权值,每个点可以赋值1、2、3,每条边连接的两个点的权值和为奇数,问给点赋权值的方案数。
题解:
1.每条边连接的两个点的权值和为奇数就是每条边连接的两个点的权值奇偶性不同,那么用染色法判定是否可以赋值。若无法做到相连的两个点权值奇偶性不同,那么就输出0。
2.这个图不一定是连通图,需要将每个连通子块的方案数相乘。
3.连通子块的方案数求法:染色后一部分点为0表示偶数,1表示奇数。统计完成后得到num[0]个0和num[1]个1,因为奇数有1和3两个可能,而偶数只有2这一个可能,所以得到方案数为2^num[1]。将每个点的权值取反得到num[1]个0和num[0]个1,又得到方案数为2^num[0]。故连通子块的方案数为2^num[0]+2^num[1]。
4.本来以为完成前三步就大功告成了,结果T了两发,搜了题解才发现memset比for循环慢!!!!!!
#include<bits/stdc++.h>
using namespace std;
#define N 300005
#define mod 998244353
vector <int> edge[N] ;
int n , m ;
bool node[N] ;
bool vis[N] ;
bool flag ;
int num[2] ;
long long cnt[N] ;
void dfs(int u)
{
int v ;
int i , j ;
num[node[u]] ++ ;
vis[u] = 1 ;
if(!flag)
return ;
for(i = 0 ; i < edge[u].size() ; i ++)
{
v = edge[u][i] ;
if(vis[v])
{
if(node[u] != node[v])
continue ;
else
{
flag = 0 ;
return ;
}
}
node[v] = 1 - node[u] ;
dfs(v) ;
}
}
void init()
{
int i ;
cnt[0] = 1 ;
for(i = 1 ; i <= N - 5 ; i ++)
cnt[i] = (cnt[i - 1] * 2) % mod ;
}
long long cal()
{
int i ;
long long ans = 0 ;
ans += cnt[num[0]] ;
ans %= mod ;
ans += cnt[num[1]] ;
ans %= mod ;
return ans ;
}
int main()
{
int t ;
int i , j ;
int u , v ;
long long sum ;
init() ;
scanf("%d" , &t) ;
while(t --)
{
scanf("%d%d" , &n , &m) ;
for(i = 1 ; i <= n ; i ++)
{
edge[i].clear() ;
vis[i] = 0 ;
}
for(i = 0 ; i < m ; i ++)
{
scanf("%d%d" , &u , &v) ;
edge[u].push_back(v) ;
edge[v].push_back(u) ;
}
sum = 1 ;
flag = 1 ;
for(i = 1 ; i <= n ; i ++)
{
if(vis[i])
continue ;
num[0] = num[1] = 0 ;
node[i] = 0 ;
dfs(i) ;
sum = (sum * (cal())) % mod ;
}
if(!flag)
printf("0\n") ;
else
printf("%lld\n" , sum) ;
}
}