点的赋值
点的赋值
给定一个 个点 条边的无向无权图。
点的编号为 。
图中不含重边和自环。
现在,请你给图中的每个点进行赋值,要求:
- 每个点的权值只能是 或 或 。
- 对于图中的每一条边,其两端点的权值之和都必须是奇数。
请问,共有多少种不同的赋值方法。
由于结果可能很大,你只需要输出对 取模后的结果。
输入格式
第一行包含整数 ,表示共有 组测试数据。
每组数据第一行包含两个整数 。
接下来 行,每行包含两个整数 ,表示点 和点 之间存在一条边。
输出格式
一个整数,表示不同赋值方法的数量对 取模后的结果。
数据范围
前三个测试点满足 ,。
所有测试点满足 ,,,,,。
输入样例:
2 2 1 1 2 4 6 1 2 1 3 1 4 2 3 2 4 3 4
输出样例:
4 0
解题思路
当时周赛写这题时想到怎么写,也写出来了,就是没注意到memset这个函数,被卡了,调了快半个小时的bug都不知道哪里出问题了。
题目要求两个点的权值之和要为偶数,意味着对于一条边的两个点,必然一个是奇数一个是偶数。
首先对于两个独立的连通块,他们之间是没有边相连的,因此我们可以分别计算每个连通块的方案数,然后根据乘法原理,总的方案数就是各个连通块的方案数的乘积。
然后由于一条边中一个点是奇数另外一个点是偶数,因此对于任意一个合法方案,我们可以把奇数点放到一边,偶数点放到一边。然后可以发现所有的边的两个点在不同的集合,如图下图:
可以发现这是一个二分图。所以如果一个图有解,即存在(染色)方案,那么这个图是一个二分图。反过来,如果一个图是二分图,那么我们可以把所有点都分到两个集合中,给这两个集合连一条边,对于任意一条边,一个点在奇数的集合中,另一个点在偶数的集合中,因此如果一个图是二分图,那么一定有解的。
所有,如果我们在染色的过程中发现这个图不是二分图,那么就说明无解,方案数为。
补充二分图的等价性质:
对于染色法判断二分图,如果一个点已确定在某个集合中,那么其他点所在的集合就唯一确定了。意味着如果一个点被染色,那么其他点的颜色是唯一确定的了。染色完成后,我们可以令染成黑色点的为奇数,染成白色的点为偶数。如果染成黑色的点有个,染成白色的点有个,由于取值为,奇数有两种取值方案,那么对于这种情况,方案数为。
或者反过来,染成白色的点是奇数,染成黑色的点为偶数。那么方案数为。因此对于这个连通的二分图,总的方案数为。接着再求其他的连通块,将所有的方案数相乘就是总的方案数。
那么如何判断每个集合(每种颜色)点的数量呢?可以通过二分图染色法,在染色的时候分别统计染黑色点的数量和染白色点的数量就可以了。
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/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16248715.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效