P4298 Solution
前置知识: 称有向图
第一问可以转化为最大权闭合子图问题. 在新图中将点
设新图是
对第二问, 相当于构造出一个最大权闭合子图. 在最大权闭合子图问题中, 一个最大权闭合子图
第三问相当于对每个
本做法只需跑一遍网络流, 由于边权均为
下面是使用了 AtCoder Library 的参考代码.
#include <cstdio>
#include <vector>
#include <atcoder/maxflow>
const int INF = 0x3F3F3F3F;
signed main() {
int n, m;
scanf("%d%d", &n, &m);
atcoder::mf_graph<int> mf(n * 2 + 2);
int S = 2 * n, T = S + 1;
for (int i = 0; i < m; ++i) {
int u, v;
scanf("%d%d", &u, &v);
--u; --v;
mf.add_edge(u + n, v, INF);
}
for (int i = 0; i < n; ++i) {
mf.add_edge(S, i + n, 1);
mf.add_edge(i, T, 1);
mf.add_edge(i, i + n, INF);
}
printf("%d\n", n - mf.flow(S, T));
auto min_cut = mf.min_cut(S);
for (int i = 0; i < n; ++i)
putchar(!min_cut[i] && min_cut[i + n] ? '1' : '0');
puts("");
std::vector<std::vector<int> > e(2 * n + 2);
for (auto o : mf.edges()) {
if (o.cap != o.flow)
e[o.from].push_back(o.to);
if (o.flow)
e[o.to].push_back(o.from);
}
std::vector<std::vector<int> > reach(2 * n + 2, std::vector<int>(2 * n + 2));
for (int i = 0; i < 2 * n + 2; ++i) {
std::queue<int> q;
q.push(i);
reach[i][i] = true;
for (; !q.empty(); q.pop())
for (auto to : e[q.front()])
if (!reach[i][to]) {
reach[i][to] = true;
q.push(to);
}
}
for (int i = 0; i < n; ++i)
putchar(!reach[i + n][T] && !reach[i + n][i] && !reach[S][i] ? '1' : '0');
puts("");
return 0;
}