「无聊的上下界最小流」 UVA1440 Inspection

皎月半洒花

2020-05-30 18:44:27

Solution

> 求解无权 DAG 的最小路径**边**覆盖。特别的,路径可以重复走。 很神奇一道题。神奇在我在 check 自己的 Sol 的时候发现了这么一回事: > 对于图中的每个点 $i$,设 $d_i$ 为( $i$ 的入度 - $i$ 的出度)的值,按照 $d_i$将图中的点分类: > > $d_i<0$ 的称为“入少出多”的点,$d_i>0$ 的称为“出少入多”的点,$d_i=0$ 的称为“入出相等”的点。则有: > > > 定理:有向无环图中最小边路径覆盖的值等于图中所有“入少出多”的点的 $d_i$ 绝对值之和。 这还是比较显然的,注意到这个定理是在陈述「边覆盖」就不难理解了。 然后借此先说 Sol 1: ### Sol 1 考虑本题和不能重复走的最小路径覆盖有什么区别,那必然是存在一些路径可以重复经过。我断言,这样的路径必然满足起点 $d_i>0$,终点的 $d_i<0$ ,否则没有必要重复经过。考虑某条这样的路径 $(s:t)$ 每被重复经过一次,就必然是为了将 $t$ 之后的某条路经和 $s$ 之前的某条路径拼插起来,这样原来需要 $2$ 次现在就只需要 $1$ 次。 于是考虑直接对着这个跑一次最大流即可,各个地方的流量都是 $1$ ,拿定理中的答案减去这个最大流就好了。 ### Sol 2 …发现这题就是一个带下界的最小流。于是就跑一个带下界的最小流即可。 具体的,就是发现每条边都必须要至少跑一次。那么就是一条 $\rm lower=1,upper=1$ 的边。随便连一下源点汇点跑一个最小流就好了。 输出方案。考虑输出方案就只需要按照流过的边 `dfs` 即可。不难知道这样是对的。 我写的是 Sol2 里的代码,因为最近正在复习上下界。 ~~ISAP即是真理,Dinic需要清除~~ 不过感觉显然 Sol2 无聊了好几倍啊? ```cpp #include <cstdio> #include <vector> #include <cstring> #include <iostream> const int N = 200010 ; const int M = 300010 ; const int Inf = 1000000007 ; using namespace std ; template <typename T> void debug(T x, char c = '\n'){ cerr << x << c ; } template <typename T> void debug(T *const tp, int minn, int maxn, char v = ' ', char c = '\n'){ for (int i = minn ; i <= maxn ; ++ i) debug(tp[i], v) ; cerr << c ; } typedef long long ll ; void chkmin(int &x, int t){ x = x < t ? x : t ; } void chkmin(ll &x, ll t){ x = x < t ? x : t ; } namespace ISAP{ #define to(k) e[k].to #define cap(k) e[k].cap #define cost(k) e[k].cost #define from(k) e[k].from #define flow(k) e[k].flow struct edge{ int from ; ll flow ; ll cap ; int to ; edge (int a = 0, int b = 0, ll c = 0){ from = a, to = b, cap = c, flow = 0 ; } } e[M] ; int n ; ll ans ; int cnt ; int S, T ; int h, t ; int que[N] ; int pre[N] ; int hgt[N] ; int cur[N] ; int gap[N] ; vector <int> E[N] ; void add(int x, int y, ll c){ e[++ cnt] = edge(x, y, c) ; E[x].push_back(cnt) ; e[++ cnt] = edge(y, x, 0) ; E[y].push_back(cnt) ; } void reset(int x, int y, int z){ memset(cur, 0, sizeof(cur)) ; memset(hgt, 0, sizeof(hgt)) ; memset(gap, 0, sizeof(gap)) ; S = x, T = y, n = z ; cnt = -1 ; for (int i = 1 ; i <= n ; ++ i) E[i].clear() ; } void prebfs(){ que[h = t = 1] = T ; while (h <= t){ int x = que[h ++] ; for (auto k : E[x]) if (!hgt[to(k)] && to(k) != T) hgt[que[++ t] = to(k)] = hgt[x] + 1 ; } for (int i = 1 ; i <= n ; ++ i) gap[hgt[i]] ++ ; } void Aug(){ int x = T ; ll z = 1ll << 60 ; while (x != S){ chkmin(z, cap(pre[x]) - flow(pre[x])) ; x = from(pre[x]) ; } ans += z ; x = T ; while (x != S){ flow(pre[x]) += z ; flow(pre[x] ^ 1) -= z ; x = from(pre[x]) ; } } bool Advance(int x, int &y){ bool ret = 0 ; for (int d = cur[x] ; d < E[x].size() ; ++ d){ int k = E[x][d] ; if (flow(k) >= cap(k)) continue ; if (hgt[x] != hgt[to(k)] + 1) continue ; cur[x] = d ; ret = 1 ; pre[y = to(k)] = k ; break ; } return ret ; } void isap(){ int x = S ; for (int i = 1 ; i <= n ; ++ i) hgt[i] = gap[i] = 0 ; prebfs() ; while (hgt[S] < n){ // cout << x << '\n' ; if (x == T) Aug(), x = S ; if (!Advance(x, x)){ int minh = n - 1 ; for (auto k : E[x]) if (flow(k) < cap(k)) chkmin(minh, hgt[to(k)]) ; if (!-- gap[hgt[x]]) return ; gap[hgt[x] = minh + 1] ++ ; cur[x] = 0 ; if (x != S) x = from(pre[x]) ; } } } } namespace YES_Lower_Upper_Minimum{ using namespace :: ISAP ; void add_e(int x, int y, int c1, int c2){ // cout << x << " " << y << " " << c1 << " " << c2 << '\n' ; add(S, y, c1) ; add(x, T, c1) ; add(x, y, c2 - c1) ; } void solve(int s, int t){ isap() ; //puts("%%%%");//debug(ans) ; add_e(t, s, 0, Inf), ans = 0 ; isap() ; cout << ans << "\n" ; } void do_back(int o){ for (int i = 0 ; i <= cnt ; ++ i) if (from(i) <= o && to(i) <= o && cap(i) > 0) ++ flow(i) ; } bool _space ; void dfs(int x, int o){ if (!_space) putchar(' ') ; else _space = 0 ; cout << x ; for (auto k : E[x]) if (to(k) <= o && flow(k) > 0){ flow(k) --, dfs(to(k), o) ; break ; } } void do_(int s, int o){ while (ans){ for (auto k : E[s]){ if (to(k) <= o){ if (flow(k)){ _space = 1 ; -- flow(k), -- ans ; dfs(to(k), o), puts("") ; } } } } } } int n, m, s, t ; using ISAP :: reset ; using YES_Lower_Upper_Minimum :: do_ ; using YES_Lower_Upper_Minimum :: dfs ; using YES_Lower_Upper_Minimum :: add_e ; using YES_Lower_Upper_Minimum :: solve ; using YES_Lower_Upper_Minimum :: do_back ; int main(){ int x, y, i ; while (scanf("%d", &n) == 1){ s = n + 1, t = n + 2 ; reset(t + 1, t + 2, t + 2) ; for (x = 1 ; x <= n ; ++ x){ scanf("%d", &i) ; while (i --){ scanf("%d", &y) ; add_e(x, y, 1, Inf) ; } add_e(s, x, 0, Inf) ; add_e(x, t, 0, Inf) ; } solve(s, t) ; do_back(n) ; do_(s, n) ; } return 0 ; } ```