HBCPC 2020 删点
题目
作者 ICPC Team
单位 北京邮电大学
小河得到了一个包含n个点的无向图。他要选取一个点的集合(可以为空),将其中的点依次删掉。对于选出的这个集合,他还需要选取一个合适的删点顺序,使得删的过程中,任何点在被删掉的时刻依然与至少一个未被删掉的点相连(请参考样例)。
删着删着,他发现,不是所有的集合都能找到一个满足条件的删点顺序。比如,对于下图:
对于集合{4},{1,4},{2,4},{3,4}都不能找出满足条件的方案,因为无论如何,在删除4的时候,它都不与任何点相连——事实上,4在任何时刻都不与其它点相连。
他希望知道,有多少个不同的集合可以找到一种满足条件的删点顺序。由于方案数可能很大,请输出答案对998244353取模后的结果。
题目保证不会出现自环。
输入格式
第一行两个数字n,m(1≤n,m≤106),表示有n个点和m条边。
接下来m行,每行两个数字x,y(1≤x,y≤n,x=y),表示x和y之间有一条边相连。
输出格式
输出存在符合条件删点顺序的集合个数
样例输入
5 4
1 2
2 3
2 4
3 5
样例输出
31
样例解释
下面是满足条件的31种方案:
∅{1},{2},{3},{4},{5},{1,2},{1,3},{1,4},{1,5},{2,3},{2,4},{2,5},{3,4},{3,5},{4,5},{1,2,3},{1,2,4},{1,2,5},{1,3,4},{1,3,5},{1,4,5},{2,3,4},{2,3,5},{2,4,5},{3,4,5},{1,2,3,4}{1,2,3,5}{1,2,4,5}{1,3,4,5}{2,3,4,5}
以{1,2,5}为例,按照1−2−5的顺序删除即可满足要求,如下图所示:
唯一一个找不到满足条件删除顺序的集合是{1,2,3,4,5},因为不管怎么删,最后一个点在删除的时候一定不与任何点相邻
代码长度限制
16 KB
Java (javac)
时间限制
6000 ms
内存限制
512 MB
其他编译器
时间限制
3000 ms
内存限制
512 MB
思路
对于图\(G<V, E>\), 考虑每个单独的连通块而言, 我们设删除集合为\(S\), 只要\(S \subset V\), 即\(S\)不包含所有点,这样的序列就是和法的,那么对于一个单独的连通块而言其方案数\(cnt_i = 2^{v_i} - 1\), 其中\(v_i\)为当前连通块内的点数, 那么所有方案数\(CNT = \prod_{i = 1}^{num}(2^{v_i} - 1)\)。
证明对于单独的连通块方案数为\(2^{v_i} - 1\) :构造一种方案,我们考虑当前图的生成树, 构造出一种合法删点顺序对于任何非全集,删点集合始终保持两种状态,有叶子节点和无叶子节点,对于叶子节点显然我们都可以直接删除, 对于非叶子节点,我们可以忽略图中该点,再去删掉别的叶子节点,另外该点若是集合中最后一个点则可以直接删除。由于全集中,我们删去\(n - 1\)个点后,最后一个点不满足条件,所以全集不是合法方案。
Code
#include <bits/stdc++.h>
using i64 = long long;
const int N = 1e6 + 10, M = 2e6 + 10, mod = 998244353;
int n, m;
int h[N], e[M], ne[M], idx;
bool st[N];
int cnt;
void add(int a, int b) {
ne[idx] = h[a], h[a] = idx, e[idx ++] = b;
}
i64 qmi(i64 a, i64 b) {
i64 res = 1;
while (b) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res % mod;
}
void dfs(int u) {
if (st[u]) return;
st[u] = true;
cnt ++;
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (st[v]) continue;
dfs(v);
}
}
int main() {
memset(h, -1, sizeof h);
std::cin >> n >> m;
for (int i = 1; i <= m; i ++) {
int x, y;
std::cin >> x >> y;
add(x, y), add(y, x);
}
i64 ans = 1;
for (int i = 1; i <= n; i ++) {
if (!st[i]) {
cnt = 0;
dfs(i);
ans = ans * (qmi(2, cnt) - 1) % mod;
}
}
std::cout << ans;
}