点的赋值

点的赋值

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

点的编号为 1n

图中不含重边和自环。

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

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

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

由于结果可能很大,你只需要输出对 998244353 取模后的结果。

输入格式

第一行包含整数 T,表示共有 T 组测试数据。

每组数据第一行包含两个整数 n,m

接下来 m 行,每行包含两个整数 u,v,表示点 u 和点 v之间存在一条边。

输出格式

一个整数,表示不同赋值方法的数量对 998244353 取模后的结果。

数据范围

前三个测试点满足 1T6i=1Tm50
所有测试点满足 1T3×1051n3×1050m3×1051u,vni=1Tn3×105i=1Tm3×105

输入样例:

复制代码
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,奇数有两种取值方案,那么对于这种情况,方案数为2x×1y=2x

  或者反过来,染成白色的点是奇数,染成黑色的点为偶数。那么方案数为2y。因此对于这个连通的二分图,总的方案数为2x+2y。接着再求其他的连通块,将所有的方案数相乘就是总的方案数。

  那么如何判断每个集合(每种颜色)点的数量呢?可以通过二分图染色法,在染色的时候分别统计染黑色点的数量和染白色点的数量就可以了。

  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 @   onlyblues  阅读(75)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示