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

x义x
“我们要走多远?”“一百万年。”搬运于
2025-08-24 22:26:14,当前版本为作者最后更新于2021-08-27 09:33:19,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题意.
给定一张 点 条边的 DAG,你可以至多添加 条有向边,使得这仍然是一个 DAG,使得字典序最小的拓扑序的字典序尽量大。
输出这个拓扑序以及方案。
。
啊又是开门一个 key obeservation 的题。
引理.
若最终拓扑序为 ,那么存在一种最优方案满足所有加的边都形如 。
证明.
否则,我们总是可以把 替换为 。说明如下。
设替换后的新排列为 。
- 显然 。这部分的拓扑排序完全和这条边没关系。
- 又可以发现 ,因为此时仍存在 。其他点的情况和该边换不换没关系,所以 。
- 如果再往后,那所有点的情况都和该边换不换没关系。
现在我们逐位确定 。如果 ,那自然是没戏唱了,直接开始拓扑排序即可。
否则,
现在将所有入度为 的节点取出,考虑其中的最小者 :
- 如果 并非唯一的入度为 元素,那我们肯定不希望 当上当前位置,所以我们希望临时加一条边把 放到后面去(让它的度数不为 )。
-
- ;显然这条边连肯定是能连的( 并非唯一的入度为 元素);至于这条边到底和谁连,我们暂时不关心。到时候只需要在求出 之后根据上面的引理编出所需的边即可。
- 如果 就是唯一的入度为 元素,那么只好把 作为当前位置,删去 。
听起来十分美好,但这个过程是明显有问题的。这个问题体现在最后【删去 】处:我们没有考虑 可能连出了一些新加边,自然也就没有更新目标节点的度,它们就永远入度为 了!
所以我们应当维护两个集合:
- 所有【明显是入度为 】的节点;
- 和所有【因为被一条新加边连入而可能现在是入度为 】的节点。
顺便,我们指出,对于这些【可能是入度为 】的节点,我们在任何时刻钦定它入度为 都是合法的:显然只要我们不认为它入度为 ,它的后继节点们就不可能被处理,从而不会连出环。
下面是修正后的流程。
现在将所有入度为 的节点取出,考虑其中的最小者 :
- 如果 并非唯一的入度为 元素,那我们肯定不希望 当上当前位置,所以我们希望临时加一条边把 放到后面去(让它的度数不为 )。
-
- ;显然这条边连肯定是能连的( 并非唯一的入度为 元素);至于这条边到底和谁连,我们暂时不关心。到时候只需要在求出 之后根据上面的引理编出所需的边即可。
- 如果 就是唯一的入度为 元素:
-
- 那么我们应当把【可能是入度为 】的节点中的最大者取出替换掉 。(当然,如果它比 小那还不如不换,还是把认定 就是当前位置并删除吧)
#include<bits/stdc++.h> using namespace std; const int maxn = 100005; int n, K, k; vector<int> G[maxn]; int deg[maxn]; priority_queue<int, vector<int>, greater<int> > U; priority_queue<int> V; void del(int u) { for (int v : G[u]) if (!(--deg[v])) U.push(v); } int p[maxn], tot; bool qaq[maxn]; int main() { int m; scanf("%d%d%d", &n, &m, &K); while (m--) { int u, v; scanf("%d%d", &u, &v); G[u].push_back(v); deg[v]++; } for (int i = 1; i <= n; i++) if (deg[i] == 0) U.push(i); while (tot != n) { if (U.empty()) { int u = V.top(); V.pop(); del(u); p[++tot] = u; continue; } int u = U.top(); U.pop(); if (k == K) { del(u); p[++tot] = u; continue; } if (!U.empty()) { k++; qaq[u] = 1; V.push(u); continue; } if (V.empty()) { del(u); p[++tot] = u; continue; } int v = V.top(); if (v < u) { del(u); p[++tot] = u; continue; } V.pop(), V.push(u), U.push(v), qaq[u] = 1, k++; } for (int i = 1; i <= n; i++) printf("%d ", p[i]); printf("\n"); printf("%d\n", k); for (int i = 2; i <= n; i++) if (qaq[p[i]]) printf("%d %d\n", p[i - 1], p[i]); }
- 1
信息
- ID
- 6208
- 时间
- 2000ms
- 内存
- 256MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者