1. 程式人生 > >luoguP3731 [HAOI2017]新型城市化 最大獨立集 二分圖的必須邊 Dinic Tarjan

luoguP3731 [HAOI2017]新型城市化 最大獨立集 二分圖的必須邊 Dinic Tarjan

luoguP3731 [HAOI2017]新型城市化

題目傳送門

分析

題目大意:給定一張恰可被劃分成兩個團的圖,求有多少種新增一條邊的方案可以使原圖最大團大小+1。
恰可被劃分成兩個團的圖       \iff 二分圖
原圖最大團大小+1   

   \iff 補圖最大獨立集大小+1       \iff 補圖最大匹配-1
問題轉化為求有多少條邊刪去後一張二分圖的最大匹配-1
事實上這條邊就是二分圖的必須邊

定義

二分圖的必須邊:刪去這邊之後二分圖的最大匹配-1

解法

建圖跑 D i n i c Dinic ,殘念網路上求 S C

C SCC ,考察每條滿流邊,如果兩個端點不在同一 S C C SCC 中這條邊即為必須邊。
嚴格證明:連結
口胡大概是因為如果不在同一個 S C C SCC 裡面刪掉之後肯定沒有增廣路,反之一定有。
類似地可以定義二分圖的可行邊:匹配這條邊之後二分圖的最大匹配不變。
顯然就是滿流邊且端點在同一個 S C C SCC 中。
其實還有一種二分圖做法:hdu4685

程式碼

#include<bits/stdc++.h>
#define mp std::make_pair
const int M = 4e5 + 10, N = 1e4 + 10;
int ri() {
    char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
    for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int be[N], s, t, n, m;
struct Edge {
    int pr[N], to[M], nx[M], w[M], tp; Edge() {tp = 1;}
    void add(int u, int v, int W) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp; w[tp] = W;}
    void adds(int u, int v) {add(u, v, 0); add(v, u, 0);}
    void ins(int u, int v, int w) {add(u, v, 1); add(v, u, 0);}
}G;
struct Tarjan {
    int dfn[N], low[N], st[N], tp, tm, tot; bool vis[N]; 
    void dfs(int u) {
        dfn[u] = low[u] = ++tm; vis[u] = true; st[++tp] = u;
        for(int i = G.pr[u], v; i; i = G.nx[i])
            if(G.w[i]) {
                v = G.to[i];
                if(!dfn[v]) dfs(v), low[u] = std::min(low[u], low[v]);
                else if(vis[v]) low[u] = std::min(low[u], dfn[v]);
            }
        if(low[u] == dfn[u])
            for(++tot;st[tp + 1] != u; vis[st[tp--]] = false)
                be[st[tp]] = tot;
    }
    void scc() {for(int i = 1;i <= t; ++i) if(!dfn[i]) dfs(i);}
}tar;
struct NetWork {
    int q[N], vis[N], D[N], tm;
    bool Bfs() {
        int L = 1, R; q[R = 1] = s; D[s] = 1; vis[s] = ++tm;
        for(int u = s; L <= R && vis[t] != tm; u = q[++L])
            for(int i = G.pr[u], v; i; i = G.nx[i])
                if(G.w[i] && vis[v = G.to[i]] != tm)
                    q[++R] = v, D[v] = D[u] + 1, vis[v] = tm;
        return vis[t] == tm;			
    }
    int Dfs(int u, int minf) {
        if(u == t) return minf;
        if(D[u] >= D[t]) return D[u] = 0;
        for(int i = G.pr[u], v, f; i; i = G.nx[i]) 
            if(G.w[i] && D[v = G.to[i]] == D[u] + 1 && (f = Dfs(v, std::min(minf, G.w[i]))))
                return G.w[i] -= f, G.w[i ^ 1] += f, f;
        return D[u] = 0;
    }
    void Dinic() {for(;Bfs();) for(;Dfs(s, 1e9););}
}net;
struct Graph {
    Edge T; std::pair<int, int>A[M]; int col[N], x[M], y[M];
    void add(int u, int v, int i) {x[i] = u; y[i] = v; T.adds(u, v);}
    void dfs(int u, int c) {
        for(int i = T.pr[u], v; i; i = T.nx[i]) 
            if(!col[v = T.to[i]]) dfs(v, col[v] = -c);
    }
    void Build() {
        for(int i = 1;i <= n; ++i) if(!col[i]) dfs(i, col[i] = -1);
        for(int i = 1;i <= m; ++i) {
            if(~col[x[i]]) G.ins(x[i], y[i], 1);
            else G.ins(y[i], x[i], 1);
        }
        for(int i = 1;i <= n; ++i) ~col[i] ? G.ins(s, i, 1) : G.ins(i, t, 1);
    }
    void Work() {
        int nw = 2; int tp = 0;
        for(int i = 1;i <= m; ++i, nw += 2) 
            if(!G.w[nw] && be[x[i]] != be[y[i]]) 
                A[++tp] = x[i] < y[i] ? mp(x[i], y[i]) : mp(y[i], x[i]);
        std::sort(A + 1, A + tp + 1); printf("%d\n", tp);
        for(int i = 1;i <= tp; ++i) printf("%d %d\n", A[i].first, A[i].second);
    }
}gra;
int main() {
    n = ri(); m = ri(); s = n + 1; t = s + 1;
    for(int i = 1;i <= m; ++i) gra.add(ri(), ri(), i); gra.Build();
    net.Dinic(); tar.scc(); gra.Work();
    return 0;
}