点的赋值

点的赋值

给定一个 $n$ 个点 $m$ 条边的无向无权图。

点的编号为 $1 \sim n$。

图中不含重边和自环。

现在,请你给图中的每个点进行赋值,要求:

  1. 每个点的权值只能是 $1$ 或 $2$ 或 $3$。
  2. 对于图中的每一条边,其两端点的权值之和都必须是奇数。

请问,共有多少种不同的赋值方法。

由于结果可能很大,你只需要输出对 $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 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 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/

posted @ 2022-05-09 15:10  onlyblues  阅读(71)  评论(0编辑  收藏  举报
Web Analytics