luoguP3731 [HAOI2017]新型城市化 最大獨立集 二分圖的必須邊 Dinic Tarjan
阿新 • • 發佈:2018-11-06
luoguP3731 [HAOI2017]新型城市化
題目傳送門
分析
題目大意:給定一張恰可被劃分成兩個團的圖,求有多少種新增一條邊的方案可以使原圖最大團大小+1。
恰可被劃分成兩個團的圖
二分圖
原圖最大團大小+1
補圖最大獨立集大小+1
補圖最大匹配-1
問題轉化為求有多少條邊刪去後一張二分圖的最大匹配-1
事實上這條邊就是二分圖的必須邊
定義
二分圖的必須邊:刪去這邊之後二分圖的最大匹配-1
解法
建圖跑
,殘念網路上求
,考察每條滿流邊,如果兩個端點不在同一
中這條邊即為必須邊。
嚴格證明:連結
口胡大概是因為如果不在同一個
裡面刪掉之後肯定沒有增廣路,反之一定有。
類似地可以定義二分圖的可行邊:匹配這條邊之後二分圖的最大匹配不變。
顯然就是滿流邊且端點在同一個
中。
其實還有一種二分圖做法: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;
}