D - Beautiful Graph CodeForces - 1093D (二分图染色+方案数)
D - Beautiful Graph
You are given an undirected unweighted graph consisting of nn vertices and mm edges.
You have to write a number on each vertex of the graph. Each number should be 11, 22or 33. The graph becomes beautiful if for each edge the sum of numbers on vertices connected by this edge is odd.
Calculate the number of possible ways to write numbers 11, 22 and 33 on vertices so the graph becomes beautiful. Since this number may be large, print it modulo 998244353998244353.
Note that you have to write exactly one number on each vertex.
The graph does not have any self-loops or multiple edges.
Input
The first line contains one integer tt (1≤t≤3⋅1051≤t≤3⋅105) — the number of tests in the input.
The first line of each test contains two integers nn and mm (1≤n≤3⋅105,0≤m≤3⋅1051≤n≤3⋅105,0≤m≤3⋅105) — the number of vertices and the number of edges, respectively. Next mm lines describe edges: ii-th line contains two integers uiui, vivi (1≤ui,vi≤n;ui≠vi1≤ui,vi≤n;ui≠vi) — indices of vertices connected by ii-th edge.
It is guaranteed that ∑i=1tn≤3⋅105∑i=1tn≤3⋅105 and ∑i=1tm≤3⋅105∑i=1tm≤3⋅105.
Output
For each test print one line, containing one integer — the number of possible ways to write numbers 11, 22, 33 on the vertices of given graph so it becomes beautiful. Since answers may be large, print them modulo 998244353998244353.
Example
Input
2
2 1
1 2
4 6
1 2
1 3
1 4
2 3
2 4
3 4
Output
4
0
Note
Possible ways to distribute numbers in the first test:
- the vertex 11 should contain 11, and 22 should contain 22;
- the vertex 11 should contain 33, and 22 should contain 22;
- the vertex 11 should contain 22, and 22 should contain 11;
- the vertex 11 should contain 22, and 22 should contain 33.
In the second test there is no way to distribute numbers.
题意:
给你n个节点,m个边的无向图。
每人一个节点可以填1,2,3 中的任意一个。
问你有多少种填数字的方案,使每一条边连接的两个节点填的数字相加为奇数。
思路:
因为一个奇数+一个偶数=奇数。
所以如果有合法的填充方案就是有合法的黑白染色方案。
那么我们先用经典的“黑白染色-判断法”判断是否能有合法方案。
如果没有直接输出0
如果可以成功黑白染色,再计算方案数。
我们通过可以推出
每一个连通块中的方案数是 2(填奇数的节点个数)+2(填偶数的节点个数)
填奇数的节点个数和偶数的个数可以通过dfs得出。
如果一个连通块中只有一个节点,那么方案数应该是3.
每一个联通的答案的乘积就是总方案数。
记得取模即可。
细节见代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <iomanip>
#define ALL(x) (x).begin(), (x).end()
#define sz(a) int(a.size())
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define pii pair<int,int>
#define pll pair<long long ,long long>
#define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define MS0(X) memset((X), 0, sizeof((X)))
#define MSC0(X) memset((X), '\0', sizeof((X)))
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define eps 1e-6
#define gg(x) getInt(&x)
#define chu(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
#define du3(a,b,c) scanf("%d %d %d",&(a),&(b),&(c))
#define du2(a,b) scanf("%d %d",&(a),&(b))
#define du1(a) scanf("%d",&(a));
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
ll lcm(ll a, ll b) {return a / gcd(a, b) * b;}
ll powmod(ll a, ll b, ll MOD) {a %= MOD; if (a == 0ll) {return 0ll;} ll ans = 1; while (b) {if (b & 1) {ans = ans * a % MOD;} a = a * a % MOD; b >>= 1;} return ans;}
void Pv(const vector<int> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%d", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}
void Pvl(const vector<ll> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%lld", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}
inline void getInt(int *p);
const int maxn = 300010;
const int inf = 0x3f3f3f3f;
/*** TEMPLATE CODE * * STARTS HERE ***/
int t;
std::vector<int> e[maxn];
queue<pii> q;
int col[maxn];
const ll mod = 998244353;
ll ans = 0ll;
int vis[maxn];
int tot;
ll base1;
ll base2;
void dfs(int x, int c)
{
vis[x] = 1;
tot++;
if (c & 1) {
base1++;
} else {
base2++;
}
for (auto z : e[x]) {
if (vis[z]) { continue; }
dfs(z, c == 1 ? 2 : 1);
}
}
int main()
{
//freopen("D:\\code\\text\\input.txt","r",stdin);
//freopen("D:\\code\\text\\output.txt","w",stdout);
du1(t);
while (t--) {
int n, m;
du2(n, m);
if (n == 1) {
n = 1;
}
repd(i, 1, n) {
e[i].clear();
col[i] = 0;
vis[i] = 0;
}
while (!q.empty()) {
q.pop();
}
int x, y;
repd(i, 1, m) {
du2(x, y);
e[x].pb(y);
e[y].pb(x);
}
int isok = 1;
// 二分图判断部分
repd(i, 1, n) {
if (col[i] != 0) {
continue;
}
q.push(mp(i, 1));
while (!q.empty()) {
pii temp = q.front();
// cout<<temp.fi<<" "<<temp.second<<endl;
q.pop();
if (col[temp.fi] != 0 && col[temp.fi] != temp.se) {
isok = 0;
break;
}
if (col[temp.fi] == 0) {
col[temp.fi] = temp.se;
} else {
continue;
}
for (auto Z : e[temp.fi]) {
q.push(mp(Z, temp.se == 1 ? 2 : 1));
}
}
}
if (isok) {
ans = 1ll;
repd(i, 1, n) {
// 对于每一个连通块,求方案数。乘法原理计算答案。
if (vis[i]) { continue; }
tot = 0;
base1 = 0ll;
base2 = 0ll;
dfs(i, 1);
if (tot == 1) {// 连通块内只有一个节点时 答案是3
ans = ans * 3ll % mod;
} else {
ans = ans * ((powmod(2ll, base1, mod) % mod + powmod(2ll, base2, mod) % mod) % mod) % mod;
}
}
printf("%lld\n", ans );
} else {
printf("0\n");
}
// cout << isok << endl;
}
return 0;
}
inline void getInt(int *p)
{
char ch;
do {
ch = getchar();
} while (ch == ' ' || ch == '\n');
if (ch == '-') {
*p = -(getchar() - '0');
while ((ch = getchar()) >= '0' && ch <= '9') {
*p = *p * 10 - ch + '0';
}
} else {
*p = ch - '0';
while ((ch = getchar()) >= '0' && ch <= '9') {
*p = *p * 10 + ch - '0';
}
}
}