点的赋值
点的赋值
给定一个 $n$ 个点 $m$ 条边的无向无权图。
点的编号为 $1 \sim n$。
图中不含重边和自环。
现在,请你给图中的每个点进行赋值,要求:
- 每个点的权值只能是 $1$ 或 $2$ 或 $3$。
- 对于图中的每一条边,其两端点的权值之和都必须是奇数。
请问,共有多少种不同的赋值方法。
由于结果可能很大,你只需要输出对 $998244353$ 取模后的结果。
输入格式
第一行包含整数 $T$,表示共有 $T$ 组测试数据。
每组数据第一行包含两个整数 $n,m$。
接下来 $m$ 行,每行包含两个整数 $u,v$,表示点 $u$ 和点 $v$之间存在一条边。
输出格式
一个整数,表示不同赋值方法的数量对 $998244353$ 取模后的结果。
数据范围
前三个测试点满足 $1 \leq T \leq 6$,$\sum\limits_{i=1}^{T} {m} \leq 50$。
所有测试点满足 $1 \leq T \leq 3 \times {10}^{5}$,$1 \leq n \leq 3 \times {10}^{5}$,$0 \leq m \leq 3 \times {10}^{5}$,$1 \leq u,v \leq n$,$\sum\limits_{i=1}^{T} {n} \leq 3 \times {10}^{5}$,$\sum\limits_{i=1}^{T} {m} \leq 3 \times {10}^{5}$。
输入样例:
2 2 1 1 2 4 6 1 2 1 3 1 4 2 3 2 4 3 4
输出样例:
4 0
解题思路
当时周赛写这题时想到怎么写,也写出来了,就是没注意到memset这个函数,被卡了,调了快半个小时的bug都不知道哪里出问题了。
题目要求两个点的权值之和要为偶数,意味着对于一条边的两个点,必然一个是奇数一个是偶数。
首先对于两个独立的连通块,他们之间是没有边相连的,因此我们可以分别计算每个连通块的方案数,然后根据乘法原理,总的方案数就是各个连通块的方案数的乘积。
然后由于一条边中一个点是奇数另外一个点是偶数,因此对于任意一个合法方案,我们可以把奇数点放到一边,偶数点放到一边。然后可以发现所有的边的两个点在不同的集合,如图下图:
可以发现这是一个二分图。所以如果一个图有解,即存在(染色)方案,那么这个图是一个二分图。反过来,如果一个图是二分图,那么我们可以把所有点都分到两个集合中,给这两个集合连一条边,对于任意一条边,一个点在奇数的集合中,另一个点在偶数的集合中,因此如果一个图是二分图,那么一定有解的。
所有,如果我们在染色的过程中发现这个图不是二分图,那么就说明无解,方案数为$0$。
补充二分图的等价性质:
对于染色法判断二分图,如果一个点已确定在某个集合中,那么其他点所在的集合就唯一确定了。意味着如果一个点被染色,那么其他点的颜色是唯一确定的了。染色完成后,我们可以令染成黑色点的为奇数,染成白色的点为偶数。如果染成黑色的点有$x$个,染成白色的点有$y$个,由于取值为$1,~2,~3$,奇数有两种取值方案,那么对于这种情况,方案数为$2^{x} \times 1^{y} = 2^{x}$。
或者反过来,染成白色的点是奇数,染成黑色的点为偶数。那么方案数为$2^{y}$。因此对于这个连通的二分图,总的方案数为$2^{x} + 2^{y}$。接着再求其他的连通块,将所有的方案数相乘就是总的方案数。
那么如何判断每个集合(每种颜色)点的数量呢?可以通过二分图染色法,在染色的时候分别统计染黑色点的数量和染白色点的数量就可以了。
AC代码如下:
1 #include2 #include 3 #include 4 using namespace std; 5 6 const int N = 3e5 + 10, M = N << 1, mod = 998244353; 7 8 int head[N], e[M], ne[M], idx; 9 int col[N]; 10 int s1, s2; 11 12 void add(int v, int w) { 13 e[idx] = w, ne[idx] = head[v], head[v] = idx++; 14 } 15 16 int pow2(int k) { 17 int ret = 1; 18 while (k--) { 19 ret = ret * 2ll % mod; 20 } 21 return ret; 22 } 23 24 bool dfs(int u, int c) { 25 col[u] = c; 26 if (c & 1) s1++; 27 else s2++; 28 29 for (int i = head[u]; i != -1; i = ne[i]) { 30 if (col[e[i]] && col[e[i]] == c) return false; 31 else if (!col[e[i]] && !dfs(e[i], 3 - c)) return false; 32 } 33 34 return true; 35 } 36 37 int main() { 38 int tot; 39 scanf("%d", &tot); 40 while (tot--) { 41 int n, m; 42 scanf("%d %d", &n, &m); 43 memset(head + 1, -1, n << 2); // 第3个参数不可以是sizeof(head),会超时。下标从1开始 44 memset(col + 1, 0, n << 2); // 同上 45 idx = 0; 46 47 while (m--) { 48 int v, w; 49 scanf("%d %d", &v, &w); 50 add(v, w), add(w, v); 51 } 52 53 int ret = 1; 54 for (int i = 1; i <= n; i++) { 55 if (!col[i]) { 56 s1 = s2 = 0; 57 if (dfs(i, 1)) { 58 ret = 1ll * ret * (pow2(s1) + pow2(s2)) % mod; 59 } 60 else { // 染色失败,说明这个图不是二分图,无解,方案数是0 61 ret = 0; 62 break; 63 } 64 } 65 } 66 67 printf("%d\n", ret); 68 } 69 70 return 0; 71 }
参考资料
AcWing 4415. 点的赋值(AcWing杯 - 周赛):https://www.acwing.com/video/3847/