【題解】新型城市化 HAOI2017 網絡流 二分圖最大匹配 強連通份量

Prelude

好,HAOI2017終於會作一道題了!
傳送到洛谷:→_→
傳送到LOJ:←_←
本篇博客連接:(●'◡'●)html


Solution

首先要讀懂題。
考場上我是這樣想的QAQ
咱們把每一個城市看做一個點,在「當前沒有貿易關係」的城市之間連邊。
此時,若是一個城市集合是一個城市羣,那麼這個城市集合中的任意兩個城市之間都沒有邊。
由於「能夠劃分爲兩個城市羣」,因此這個圖是個二分圖。
那麼「最大城市羣」就是二分圖的最大獨立集。
「在兩個城市之間創建貿易關係」即刪除這兩個點之間的邊。
因此題目其實是,給一個二分圖,問刪掉哪些邊以後,最大獨立集的大小會增長。
考慮如何求最大獨立集大小。
最大獨立集大小=總點數-最小覆蓋集大小=最大匹配數。
也就是說,這個題問的是,給一個二分圖,問刪掉哪些邊以後,最大匹配的數量會減小,也就是問,哪些邊必定在最大匹配裏。
這個時候,咱們已經獲得了50分作法了。
先建出網絡流,求出最大匹配數量,而後刪掉一條邊從新跑一次,看最大匹配是否減小,就是我考場上的作法。
用退流能夠作到更優越的複雜度,但好像過不了n=500的點?
接下來考慮滿分作法。
考慮以下定理:若一條邊必定在最大匹配中,則在最終的殘量網絡中,這條邊必定滿流,且這條邊的兩個頂點必定不在同一個強連通份量中。
證實也很簡單:首先滿流的要求是很顯然的,其次,若是這兩個點在同一個強連通份量中,那麼必定有一個環通過這條邊,沿着環增廣一下,網絡仍然知足流量限制,可是這條邊就不滿流了,因而就獲得了一組新的最大匹配。
因此只要跑完Dinic跑Tarjan就行了。網絡


Code

#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <utility>

using namespace std;
typedef pair<int,int> pii;
const int MAXN = 10010;
const int MAXM = 150010;
const int MAXV = 100010;
const int MAXE = 1000010;
const int INF = 0x3f3f3f3f;
int _w;

int n, m, uu[MAXM], vv[MAXM];

namespace G {
    int head[MAXN], nxt[MAXM<<1], to[MAXM<<1], eid;
    void init() {
        eid = 0;
        memset(head, -1, sizeof head);
    }
    void adde( int u, int v ) {
        to[eid] = v, nxt[eid] = head[u], head[u] = eid++;
        to[eid] = u, nxt[eid] = head[v], head[v] = eid++;
    }
}

namespace Dinic {
    struct Edge {
        int u, v, c, f;
        Edge() {}
        Edge( int u, int v, int c, int f ):
            u(u), v(v), c(c), f(f) {}
    };
    
    int n, m, s, t;
    int head[MAXV], nxt[MAXE<<1];
    Edge edge[MAXE<<1];
    int dis[MAXV], cur[MAXV];
    queue<int> q;
    
    void init( int _n ) {
        n = _n, m = 0;
        for( int i = 0; i < n; ++i )
            head[i] = -1;
    }
    int adde( int u, int v, int c ) {
        int eid = m;
        edge[m] = Edge(u, v, c, 0);
        nxt[m] = head[u], head[u] = m++;
        edge[m] = Edge(v, u, 0, 0);
        nxt[m] = head[v], head[v] = m++;
        return eid;
    }
    bool bfs() {
        for( int i = 0; i < n; ++i )
            dis[i] = INF;
        dis[s] = 0, q.push(s);
        while( !q.empty() ) {
            int u = q.front(); q.pop();
            for( int i = head[u]; ~i; i = nxt[i] ) {
                Edge &e = edge[i];
                if( e.c > e.f && dis[e.v] == INF ) {
                    dis[e.v] = dis[u] + 1;
                    q.push(e.v);
                }
            }
        }
        return dis[t] != INF;
    }
    int dfs( int u, int res ) {
        if( u == t || !res ) return res;
        int flow = 0;
        for( int &i = cur[u]; ~i; i = nxt[i] ) {
            Edge &e = edge[i];
            if( e.c > e.f && dis[e.v] == dis[u] + 1 ) {
                int f = dfs( e.v, min(res, e.c-e.f) );
                flow += f, res -= f;
                e.f += f, edge[i^1].f -= f;
                if( !res ) break;
            }
        }
        return flow;
    }
    int solve( int _s, int _t ) {
        s = _s, t = _t;
        int flow = 0;
        while( bfs() ) {
            for( int i = 0; i < n; ++i )
                cur[i] = head[i];
            flow += dfs(s, INF);
        }
        return flow;
    }
}

namespace Bipartite {
    int color[MAXN], eid[MAXM];
    queue<int> q;
    
    void bfs( int s ) {
        using namespace G;
        
        color[s] = 0, q.push(s);
        while( !q.empty() ) {
            int u = q.front(); q.pop();
            for( int i = head[u]; ~i; i = nxt[i] ) {
                int v = to[i];
                if( color[v] == -1 ) {
                    color[v] = !color[u];
                    q.push(v);
                }
            }
        }
    }
    void bipartite() {
        for( int i = 1; i <= n; ++i )
            color[i] = -1;
        for( int i = 1; i <= n; ++i )
            if( color[i] == -1 )
                bfs(i);
        int s = 0, t = n+1;
        Dinic::init(t+1);
        for( int i = 1; i <= n; ++i )
            if( color[i] ) Dinic::adde(s, i, 1);
            else Dinic::adde(i, t, 1);
        for( int i = 0; i < m; ++i )
            if( color[uu[i]] )
                eid[i] = Dinic::adde( uu[i], vv[i], 1 );
            else
                eid[i] = Dinic::adde( vv[i], uu[i], 1 );
        Dinic::solve(s, t);
    }
}
using Bipartite::bipartite;

namespace Tarjan {
    using namespace Dinic;
    
    int dfn[MAXV], low[MAXV], scc[MAXV], dfnc, sccc;
    stack<int> stk;
    
    void dfs( int u ) {
        dfn[u] = low[u] = ++dfnc;
        stk.push(u);
        for( int i = head[u]; ~i; i = nxt[i] ) {
            Edge &e = edge[i];
            if( e.c == e.f ) continue;
            int v = e.v;
            if( !dfn[v] ) {
                dfs(v);
                low[u] = min( low[u], low[v] );
            } else if( !scc[v] ) {
                low[u] = min( low[u], dfn[v] );
            }
        }
        if( low[u] == dfn[u] ) {
            ++sccc;
            while(1) {
                int o = stk.top(); stk.pop();
                scc[o] = sccc;
                if( o == u ) break;
            }
        }
    }
    void tarjan() {
        dfnc = sccc = 0;
        for( int i = 0; i < Dinic::n; ++i )
            if( !dfn[i] ) dfs(i);
    }
}
using Tarjan::tarjan;

namespace Solve {
    vector<pii> ans;
    void solve() {
        using Dinic::Edge;
        using Dinic::edge;
        using Tarjan::scc;
        using Bipartite::eid;
        
        for( int i = 0; i < m; ++i ) {
            Edge &e = edge[eid[i]];
            if( e.c != e.f ) continue;
            int u = e.u, v = e.v;
            if( u > v ) swap(u, v);
            if( scc[u] == scc[v] ) continue;
            ans.push_back( pii(u, v) );
        }
        sort(ans.begin(), ans.end());
        printf( "%lu\n", ans.size() );
        for( int i = 0; i < (int)ans.size(); ++i )
            printf( "%d %d\n", ans[i].first, ans[i].second );
    }
}
using Solve::solve;

int main() {
    _w = scanf( "%d%d", &n, &m );
    G::init();
    for( int i = 0; i < m; ++i ) {
        _w = scanf( "%d%d", uu+i, vv+i );
        G::adde( uu[i], vv[i] );
    }
    bipartite();
    tarjan();
    solve();
    return 0;
}
相關文章
相關標籤/搜索