luoguP3731 [HAOI2017]新型城市化 最大独立集 二分图的必须边 Dinic Tarjan

luoguP3731 [HAOI2017]新型城市化

题目传送门

分析

题目大意:给定一张恰可被划分成两个团的图,求有多少种添加一条边的方案可以使原图最大团大小+1。
恰可被划分成两个团的图    ⟺    \iff 二分图
原图最大团大小+1    ⟺    \iff 补图最大独立集大小+1    ⟺    \iff 补图最大匹配-1
问题转化为求有多少条边删去后一张二分图的最大匹配-1
事实上这条边就是二分图的必须边

定义

二分图的必须边:删去这边之后二分图的最大匹配-1

解法

建图跑 D i n i c Dinic Dinic,残念网络上求 S C C SCC SCC,考察每条满流边,如果两个端点不在同一 S C C SCC SCC中这条边即为必须边。
严格证明:链接
口胡大概是因为如果不在同一个 S C C SCC SCC里面删掉之后肯定没有增广路,反之一定有。
类似地可以定义二分图的可行边:匹配这条边之后二分图的最大匹配不变。
显然就是满流边且端点在同一个 S C C SCC 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值