CF847J
这咋想到网络流的啊。。
由最大值最小想到二分最大值,将求解转化为判定。
然后假设当前 check 的值为 \(x\),从源点向每个点连一条容量为 \(x\) 的边,表示这个点至多送出 \(x\) 个礼物。
然后把每个关系也看成一个点,假设当前是第 \(i\) 条关系 \((a_i,b_i)\),那么 \(a_i\) 和 \(b_i\) 都向 \(i^{′}\) 连一条容量为 \(1\) 的边,\(i^{′}\) 向汇点连一条容量为 \(1\) 的边,代表要么 \(a_i\to b_i\),要么 \(b_i\to a_i\)。
然后判定就是看有没有满流。
具体细节看代码。
Code:
#include <bits/stdc++.h>
using namespace std;
const int N = 10005, M = 40005, inf = 0x3f3f3f3f;
int n, m;
int u[N], v[N];
int head[N], now[N], ver[M], wei[M], nxt[M], cnt = 1;
int S, T, dep[N];
int maxflow;
queue <int> q;
void add(int u, int v, int w) {
ver[++cnt] = v, wei[cnt] = w, nxt[cnt] = head[u], head[u] = cnt;
ver[++cnt] = u, wei[cnt] = 0, nxt[cnt] = head[v], head[v] = cnt;
}
void init() { memset(head, 0, sizeof head), cnt = 1; }
bool bfs() {
memset(dep, 0, sizeof dep), dep[S] = 1;
while (!q.empty()) q.pop(); q.push(S), now[S] = head[S];
while (!q.empty()) {
int u = q.front(); q.pop();
for (int i = head[u]; i; i = nxt[i]) {
int v = ver[i], w = wei[i];
if (w && !dep[v]) {
dep[v] = dep[u] + 1, now[v] = head[v], q.push(v);
if (v == T) return true;
}
}
}
return false;
}
int Dinic(int u, int flow) {
if (u == T) return flow;
int res = flow;
for (int &i = now[u]; i; i = nxt[i]) {
int v = ver[i], w = wei[i];
if (w && dep[v] == dep[u] + 1) {
int k = Dinic(v, min(res, w));
if (!k) dep[v] = 0;
wei[i] -= k, wei[i ^ 1] += k, res -= k;
}
if (!res) break;
}
return flow - res;
}
bool check(int k) {
init();
for (int i = 1; i <= m; ++i) add(u[i], i + n, 1), add(v[i], i + n, 1), add(i + n, T, 1);
for (int i = 1; i <= n; ++i) add(S, i, k);
int ans = 0, flow;
while (bfs())
while (flow = Dinic(S, inf)) ans += flow;
return ans == m;
}
int main() {
scanf("%d%d", &n, &m), S = 0, T = n + m + 1;
for (int i = 1; i <= m; ++i) scanf("%d%d", &u[i], &v[i]);
int l = 0, r = m;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
printf("%d\n", l);
check(l);
for (int i = 1; i <= m; ++i) {
if (wei[i * 6 - 4]) printf("%d %d\n", v[i], u[i]);
else printf("%d %d\n", u[i], v[i]);
}
return 0;
}