1 条题解

  • 0
    @ 2025-8-24 22:09:12

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar _sys
    goodbye, OI

    搬运于2025-08-24 22:09:12,当前版本为作者最后更新于2021-07-05 15:49:05,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    给定一张图,你需要选出一个子集,使得这个子集没有环,子集的补集没有环。

    T10T \leq 10n500n \leq 500m103m \leq 10^3


    一张图是合法的当且仅当其所有导出子图 (V,E)(V, E) 满足 E2V2|E| \leq 2|V|-2

    必要性显然。充分性考虑归纳法。我们通过点数为 V1|V|-1 的图成立推出点数为 V|V| 的成立。

    d(u)d(u) 表示 uu 的度数,我们取 dd 最小的一个作为我们加入的点 uu

    那么 1d(u)31 \leq d(u) \leq 3。否则 d=4V\sum d=4|V|E=2V|E|=2|V|,不满足条件。

    d(u)2d(u) \leq 2,我们设相邻的点为 v1,v2v_1, v_2,把 (v1,u)(v_1, u) 加入第一个森林,(v2,u)(v_2, u) 加入第二个森林,仍然满足性质。

    d(u)=3d(u)=3,设相邻的点为 v1,v2,v3v_1, v_2, v_3。那么去掉 uu 及其邻边的所有图 GG 满足 E<2V2|E|<2|V|-2

    我们考虑三个导出子图 H1(E1,V1)H_1(E_1, V_1), H2(E2,V2)H_2(E_2, V_2), H3(E3,V3)H_3(E_3, V_3)H1H_1 不包含 (u,v1)(u, v_1) 但包含 v2,v3v_2, v_3H2H_2 不包含 (u,v2)(u, v_2) 但包含 v1,v3v_1, v_3H3H_3 不包含 (u,v3)(u, v_3) 但包含 v1,v2v_1, v_2。这样的 H1,H2,H3H_1, H_2, H_3 有很多种。但至少有一个 ii 满足不存在 HiH_i 使得 Ei=2Vi2|E_i|=2|V_i|-2

    引理:若 G1(E1,V1),G2(E2,V2)G_1(E_1, V_1), G_2(E_2, V_2) 满足 E1=2V12,E2=2V22|E_1|=2|V_1|-2, |E_2|=2|V_2|-2,则 G1G2(E1,V1),G1G2(E2,V2)G_1 \cap G_2(E'_1, V'_1), G_1 \cup G_2(E'_2, V'_2) 满足 E1=2V12,E2=2V22|E_1'|=2|V_1'|-2, |E_2'|=2|V_2'|-2。因为交 + 并 = 和,假如一个没达到上界,另一个就要超过上界。

    使用反证法,若三个 HH 都能达到上界,那他们的并也达到了上界,而他们的并,就是 GG。矛盾。

    那么假设 H1H_1 达不到上界,那么我们在 H1H_1 中加入一条边 (v2,v3)(v_2, v_3),这样 H1H'_1 也是合法的。

    于是我们考虑这样一张图:在 (V,E)(V, E) 的基础上,除去 uu 的出边,加入 (v2,v3)(v_2, v_3),根据归纳假设,它能够分成两个森林。

    我们设第一个森林包含了 (v2,v3)(v_2, v_3),那么我们把这条边删去,加入 (u,v2),(u,v3)(u, v_2), (u, v_3),它还是一个森林。第二个森林加入 (u,v1)(u, v_1),它还是一个森林。

    原命题得证。

    也就是说,我们只用判断这张图是否满足对于所有的子图满足 E2V2|E| \leq 2|V|-2

    也就是说,max{E2V}2\max\{|E|-2|V|\} \leq -2

    这是一个最大权闭合子图的模型。考虑一条边的权值为 11,一个点的权值为 2-2,我们要求的是最大权闭合子图。

    但是有一个问题!这个子图必须非空,因为空的图被判定为不合法了。

    所以我们钦定一个点一定被选,把它在网络流中的点删去,最后把答案直接 2-2 即可。

    这样的复杂度是 O(nmn)O(nm \sqrt n) 的。

    (好像这样就能过了,但是 uoj 那题就过不了)

    考虑优化,发现每次是删一条边后的最大流。使用退流即可。

    时间复杂度:O(nm)O(nm)

    #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
    上传者