1 条题解
-
0
自动搬运
来自洛谷,原作者为

_sys
goodbye, OI搬运于
2025-08-24 22:09:12,当前版本为作者最后更新于2021-07-05 15:49:05,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
给定一张图,你需要选出一个子集,使得这个子集没有环,子集的补集没有环。
,,。
一张图是合法的当且仅当其所有导出子图 满足 。
必要性显然。充分性考虑归纳法。我们通过点数为 的图成立推出点数为 的成立。
设 表示 的度数,我们取 最小的一个作为我们加入的点 。
那么 。否则 ,,不满足条件。
若 ,我们设相邻的点为 ,把 加入第一个森林, 加入第二个森林,仍然满足性质。
若 ,设相邻的点为 。那么去掉 及其邻边的所有图 满足 。
我们考虑三个导出子图 , , , 不包含 但包含 , 不包含 但包含 , 不包含 但包含 。这样的 有很多种。但至少有一个 满足不存在 使得 。
引理:若 满足 ,则 满足 。因为交 + 并 = 和,假如一个没达到上界,另一个就要超过上界。
使用反证法,若三个 都能达到上界,那他们的并也达到了上界,而他们的并,就是 。矛盾。
那么假设 达不到上界,那么我们在 中加入一条边 ,这样 也是合法的。
于是我们考虑这样一张图:在 的基础上,除去 的出边,加入 ,根据归纳假设,它能够分成两个森林。
我们设第一个森林包含了 ,那么我们把这条边删去,加入 ,它还是一个森林。第二个森林加入 ,它还是一个森林。
原命题得证。
也就是说,我们只用判断这张图是否满足对于所有的子图满足 。
也就是说,。
这是一个最大权闭合子图的模型。考虑一条边的权值为 ,一个点的权值为 ,我们要求的是最大权闭合子图。
但是有一个问题!这个子图必须非空,因为空的图被判定为不合法了。
所以我们钦定一个点一定被选,把它在网络流中的点删去,最后把答案直接 即可。
这样的复杂度是 的。
(好像这样就能过了,但是 uoj 那题就过不了)
考虑优化,发现每次是删一条边后的最大流。使用退流即可。
时间复杂度:。
#include <bits/stdc++.h> using namespace std; const int Maxn = 12005; int T, n, m, ct, cnt, s, t, ans, cur[Maxn], dis[Maxn], head[Maxn]; struct edg { int nxt, to, w; } edge[10 * Maxn]; void add(int x, int y, int w) { edge[++cnt] = (edg){head[x], y, w}; head[x] = cnt; edge[++cnt] = (edg){head[y], x, 0}; head[y] = cnt; } queue <int> Qu; bool bfs(void) { Qu.push(s); memset(dis, 0, sizeof(int[ct + 1])); while (!Qu.empty()) { int u = Qu.front(); Qu.pop(); if (u == t) { while (!Qu.empty()) Qu.pop(); return true; } for (int i = head[u]; i; i = edge[i].nxt) { int to = edge[i].to; if (to != s && !dis[to] && edge[i].w) dis[to] = dis[u] + 1, Qu.push(to); } } return false; } int dfs(int u, int mini) { if (u == t || !mini) return mini; int w, used = 0; for (int & i = cur[u]; i; i = edge[i].nxt) { int to = edge[i].to; if (dis[to] == dis[u] + 1 && edge[i].w) { w = dfs(to, min(mini - used, edge[i].w)); edge[i].w -= w, edge[((i - 1) ^ 1) + 1].w += w, used += w; if (used == mini) return used; } } return used; } void dinic(int w) { while (bfs()) { memcpy(cur, head, sizeof(int[ct + 1])); ans += w * dfs(s, 0x3f3f3f3f); } } int main() { scanf("%d", &T); while (T--) { cnt = ans = 0; memset(head, 0, sizeof(int[ct + 1])); scanf("%d%d", &n, &m); ct = n; s = ++ct, t = ++ct; for (int i = 1; i <= n; i++) add(i, t, 2); for (int i = 1; i <= m; i++) { int x, y; scanf("%d%d", &x, &y); ++ct; add(s, ct, 1); add(ct, x, 0x3f3f3f3f), add(ct, y, 0x3f3f3f3f); } dinic(1); for (int i = 1; i <= 2 * n; i += 2) { s = (i + 1) / 2, t = n + 1, dinic(-1); edge[i].w = 0; s = n + 1, t = n + 2, dinic(1); if (m - ans >= 1) { puts("No"); goto END; } edge[i].w = 2; } puts("Yes"); END:; } return 0; }
- 1
信息
- ID
- 4281
- 时间
- 2000ms
- 内存
- 250MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者