2025-5-21 网络流做题笔记
2025-5-21 网络流做题笔记
le0n 的讲课 111
CF2046 D - For the Emperor!
先把强连通分量缩了,接下来考虑下放所有的信使,每个点拆成入点和出点,从入点流到出点表示有带信的信使经过,那么跑费用流即可。
ケロシの代码
const int N = 2e2 + 5;
const int M = 8e2 + 5;
const int V = 1e3;
const int INF = 1e9 + 7;
int n, m, a[N], b[N];
int fi[N], ne[M], to[M], ecnt;
int dfn[N], low[N], cnt;
int stk[N], vis[N], tp;
int sc, scc[N];
int gp(int u, int o) {
return u + o * sc;
}
void add(int u, int v) {
ne[++ ecnt] = fi[u];
to[ecnt] = v;
fi[u] = ecnt;
}
void tarjan(int u) {
dfn[u] = low[u] = ++ cnt;
stk[++ tp] = u;
vis[u] = 1;
for(int i = fi[u]; i; i = ne[i]) {
int v = to[i];
if(! dfn[v]) {
tarjan(v);
chmin(low[u], low[v]);
}
else if(vis[v]) {
chmin(low[u], dfn[v]);
}
}
if(dfn[u] == low[u]) {
sc ++;
while(1) {
scc[stk[tp]] = sc;
vis[stk[tp]] = 0;
tp --;
if(stk[tp + 1] == u) break;
}
}
}
namespace Mincost {
const int N = 6e2 + 5;
const int M = 1e4 + 5;
const int INF = 0x3f3f3f3f;
struct edge {
int ew, ct, ne, to;
} e[M];
int fi[N], ecnt;
int S, T;
int d[N], f[N], p[N];
bool vis[N];
void init() {
memset(fi, 0, sizeof fi);
ecnt = 1;
}
void add(int u, int v, int w, int c) {
e[++ ecnt] = {w, c, fi[u], v};
fi[u] = ecnt;
e[++ ecnt] = {0, - c, fi[v], u};
fi[v] = ecnt;
}
bool spfa() {
memset(d, 0x3f, sizeof d);
memset(vis, 0, sizeof vis);
queue<int> q;
d[S] = 0; f[S] = INF;
vis[S] = 1; q.push(S);
while(! q.empty()) {
int u = q.front();
q.pop(); vis[u] = 0;
for(int i = fi[u]; i; i = e[i].ne) if(e[i].ew) {
int v = e[i].to;
if(d[v] > d[u] + e[i].ct) {
d[v] = d[u] + e[i].ct;
f[v] = min(f[u], e[i].ew);
p[v] = i;
if(! vis[v]) {
vis[v] = 1;
q.push(v);
}
}
}
}
return d[T] != INF;
}
PII mincost(int _S, int _T) {
S = _S, T = _T;
int flow = 0, cost = 0;
while(spfa()) {
flow += f[T];
cost += f[T] * d[T];
int u = T;
while(u != S) {
int v = p[u];
e[v].ew -= f[T];
e[v ^ 1].ew += f[T];
u = e[v ^ 1].to;
}
}
return {flow, cost};
}
}
void solve() {
cin >> n >> m;
FOR(i, 1, n) cin >> a[i];
ecnt = 0;
FOR(i, 1, n) fi[i] = 0;
REP(_, m) {
int u, v;
cin >> u >> v;
add(u, v);
}
cnt = tp = sc = 0;
FOR(i, 1, n) dfn[i] = low[i] = vis[i] = 0;
FOR(i, 1, n) if(! dfn[i]) tarjan(i);
FOR(i, 1, sc) b[i] = 0;
FOR(i, 1, n) b[scc[i]] += a[i];
Mincost :: init();
int S = 0, T = sc * 3 + 1;
FOR(i, 1, sc) {
if(b[i]) {
Mincost :: add(S, gp(i, 0), b[i], 0);
Mincost :: add(gp(i, 0), gp(i, 1), 1, 1);
Mincost :: add(gp(i, 0), gp(i, 2), INF, 0);
}
Mincost :: add(gp(i, 1), gp(i, 2), 1, - V);
Mincost :: add(gp(i, 1), gp(i, 2), INF, 0);
Mincost :: add(gp(i, 2), T, INF, 0);
}
FOR(u, 1, n) for(int i = fi[u]; i; i = ne[i]) {
int v = to[i];
if(scc[u] != scc[v])
Mincost :: add(gp(scc[u], 2), gp(scc[v], 1), INF, 0);
}
auto h = Mincost :: mincost(S, T);
if(SE(h) >= - V * (sc - 1)) cout << - 1 << endl;
else cout << V + (SE(h) % V) << endl;
}
ICPC 2023 Polish E - Express Eviction
考虑将原图对偶,那么有一条路径相当于对偶图上左下到右上不连通,直接最小割即可。
ケロシの代码
namespace Dinic {
const int N = 5e3 + 5;
const int M = 1e6 + 5;
const int INF = 0x3f3f3f3f;
struct Edge { int ne, to, ew; } e[M];
int fi[N], ecnt;
int S, T, d[N], c[N];
void init() {
ecnt = 1;
memset(fi, 0, sizeof fi);
}
void add(int u, int v, int w) {
e[++ ecnt] = {fi[u], v, w};
fi[u] = ecnt;
e[++ ecnt] = {fi[v], u, 0};
fi[v] = ecnt;
}
bool bfs() {
memset(d, 0x3f, sizeof d);
queue<int> q;
d[S] = 0; q.push(S);
while(! q.empty()) {
int u = q.front();
q.pop();
for(int i = fi[u]; i; i = e[i].ne) if(e[i].ew) {
int v = e[i].to;
if(d[v] == INF) {
d[v] = d[u] + 1;
q.push(v);
}
}
}
return d[T] != INF;
}
int dfs(int u, int w) {
if(u == T || ! w) return w;
int res = 0;
for(int & i = c[u]; i; i = e[i].ne) if(e[i].ew) {
int v = e[i].to;
if(d[v] != d[u] + 1) continue;
int val = dfs(v, min(w, e[i].ew));
if(! val) continue;
e[i].ew -= val;
e[i ^ 1].ew += val;
w -= val;
res += val;
if(! w) return res;
}
return res;
}
int dinic(int _S, int _T) {
S = _S, T = _T;
int res = 0;
while(bfs()) {
memcpy(c, fi, sizeof c);
res += dfs(S, INF);
}
return res;
}
}
const int N = 55;
const int INF = 1e9 + 7;
int n, m;
string s[N];
int gp(int x, int y, int o) {
return (x - 1) * m + y + o * n * m;
}
void solve() {
cin >> n >> m;
FOR(i, 1, n) {
cin >> s[i];
s[i] = ' ' + s[i];
}
int S = 0, T = gp(n, m, 1) + 1;
Dinic :: init();
FOR(i, 1, n) FOR(j, 1, m) if(s[i][j] == '#') {
Dinic :: add(gp(i, j, 0), gp(i, j, 1), 1);
if(j == 1 || i == n) Dinic :: add(S, gp(i, j, 0), INF);
if(i == 1 || j == m) Dinic :: add(gp(i, j, 1), T, INF);
FOR(dx, - 2, 2) FOR(dy, - 2, 2) {
int x = i + dx, y = j + dy;
if(x < 1 || x > n || y < 1 || y > m) continue;
if(s[x][y] != '#') continue;
Dinic :: add(gp(i, j, 1), gp(x, y, 0), INF);
}
}
cout << Dinic :: dinic(S, T) << endl;
}
ABC397 G - Maximize Distance
首先考虑想要最段路变成 \(1\),那么就是一个最小割。
考虑扩展,每个点拆成一些距离点,表示 \(u\) 的最段路大于等于 \(d\)。
那么 \((u,d) \to (v,d+1)\) 是没法割的,\((u,d) \to (v,d)\) 割掉需要 \(1\) 的代价。
不难发现一对点直接不会被割掉两次。
ケロシの代码
namespace Dinic {
const int N = 1e3 + 5;
const int M = 1e5 + 5;
const int INF = 0x3f3f3f3f;
struct Edge { int ne, to, ew; } e[M];
int fi[N], ecnt;
int S, T, d[N], c[N];
void init() {
ecnt = 1;
memset(fi, 0, sizeof fi);
}
void add(int u, int v, int w) {
e[++ ecnt] = {fi[u], v, w};
fi[u] = ecnt;
e[++ ecnt] = {fi[v], u, 0};
fi[v] = ecnt;
}
bool bfs() {
memset(d, 0x3f, sizeof d);
queue<int> q;
d[S] = 0; q.push(S);
while(! q.empty()) {
int u = q.front();
q.pop();
for(int i = fi[u]; i; i = e[i].ne) if(e[i].ew) {
int v = e[i].to;
if(d[v] == INF) {
d[v] = d[u] + 1;
q.push(v);
}
}
}
return d[T] != INF;
}
int dfs(int u, int w) {
if(u == T || ! w) return w;
int res = 0;
for(int & i = c[u]; i; i = e[i].ne) if(e[i].ew) {
int v = e[i].to;
if(d[v] != d[u] + 1) continue;
int val = dfs(v, min(w, e[i].ew));
if(! val) continue;
e[i].ew -= val;
e[i ^ 1].ew += val;
w -= val;
res += val;
if(! w) return res;
}
return res;
}
int dinic(int _S, int _T) {
S = _S, T = _T;
int res = 0;
while(bfs()) {
memcpy(c, fi, sizeof c);
res += dfs(S, INF);
}
return res;
}
}
const int M = 1e2 + 5;
const int INF = 1e9 + 7;
int n, m, k;
int fu[M], fv[M];
int gp(int x, int y) {
return x + y * n;
}
void solve() {
cin >> n >> m >> k;
FOR(i, 1, m) cin >> fu[i] >> fv[i];
Dinic :: init();
int S = gp(1, 0), T = gp(n, n);
FOR(u, 1, n) FOR(i, 1, n) Dinic :: add(gp(u, i - 1), gp(u, i), INF);
int flow = 0;
FOR(i, 1, n) {
FOR(j, 1, m) Dinic :: add(gp(fu[j], i - 1), gp(fv[j], i - 1), 1);
flow += Dinic :: dinic(S, T);
if(flow > k) {
cout << i - 1 << endl;
return;
}
FOR(j, 1, m) Dinic :: add(gp(fu[j], i - 1), gp(fv[j], i), INF);
}
}
ARC142 E - Pairing Wizards
考虑先把 \(a_x,a_y\) chmax 成 \(\min(b_x,b_y)\),这样接下来只要 \(a_x,a_y\) 有一个大于 \(\max(b_x,b_y)\) 即可。
不难发现如果 \(a_x < b_x\),那么 \(\max(b_x,b_y)\) 即为 \(b_x\),所以有两种选择:将 \(a_x\) 提到 \(b_x\),或者将所有 \(a_y\) 提到 \(b_x\),直接最小割即可。
ケロシの代码
namespace Dinic {
const int N = 1.1e4 + 5;
const int M = 1e5 + 5;
const int INF = 0x3f3f3f3f;
struct Edge { int ne, to, ew; } e[M];
int fi[N], ecnt;
int S, T, d[N], c[N];
void init() {
ecnt = 1;
memset(fi, 0, sizeof fi);
}
void add(int u, int v, int w) {
e[++ ecnt] = {fi[u], v, w};
fi[u] = ecnt;
e[++ ecnt] = {fi[v], u, 0};
fi[v] = ecnt;
}
bool bfs() {
memset(d, 0x3f, sizeof d);
queue<int> q;
d[S] = 0; q.push(S);
while(! q.empty()) {
int u = q.front();
q.pop();
for(int i = fi[u]; i; i = e[i].ne) if(e[i].ew) {
int v = e[i].to;
if(d[v] == INF) {
d[v] = d[u] + 1;
q.push(v);
}
}
}
return d[T] != INF;
}
int dfs(int u, int w) {
if(u == T || ! w) return w;
int res = 0;
for(int & i = c[u]; i; i = e[i].ne) if(e[i].ew) {
int v = e[i].to;
if(d[v] != d[u] + 1) continue;
int val = dfs(v, min(w, e[i].ew));
if(! val) continue;
e[i].ew -= val;
e[i ^ 1].ew += val;
w -= val;
res += val;
if(! w) return res;
}
return res;
}
int dinic(int _S, int _T) {
S = _S, T = _T;
int res = 0;
while(bfs()) {
memcpy(c, fi, sizeof c);
res += dfs(S, INF);
}
return res;
}
}
const int N = 1e2 + 5;
const int M = 1e4 + 5;
const int V = 100;
const int INF = 1e9 + 7;
int n, A[N], a[N], b[N];
int m, fu[M], fv[M];
int gp(int x, int y) {
return x + y * n;
}
void solve() {
cin >> n;
FOR(i, 1, n) {
cin >> A[i] >> b[i];
a[i] = A[i];
}
cin >> m;
FOR(i, 1, m) {
cin >> fu[i] >> fv[i];
int x = min(b[fu[i]], b[fv[i]]);
chmax(a[fu[i]], x);
chmax(a[fv[i]], x);
}
int ans = 0;
FOR(i, 1, n) ans += a[i] - A[i];
Dinic :: init();
int S = 0, T = gp(n, V) + 1;
FOR(i, 1, n) FOR(j, 2, V) Dinic :: add(gp(i, j), gp(i, j - 1), INF);
FOR(i, 1, n) FOR(j, a[i] + 1, V) Dinic :: add(gp(i, j), T, 1);
FOR(i, 1, n) if(a[i] < b[i]) {
Dinic :: add(S, i, b[i] - a[i]);
FOR(j, 1, m) {
int u = fu[j], v = fv[j];
if(u == i) Dinic :: add(i, gp(v, b[i]), INF);
if(v == i) Dinic :: add(i, gp(u, b[i]), INF);
}
}
ans += Dinic :: dinic(S, T);
cout << ans << endl;
}
CF1427 G - One Billion Shades of Grey
考虑绝对值转化成 \(\sum_k [a<k][b \ge k]\),就可以跑流了。
考虑枚举 \(k\),每次直接退流即可。
ケロシの代码
const int N = 2e2 + 5;
const int M = 1e6 + 5;
int n, a[N][N];
struct Edge { int ne, to, ew; } e[M];
int fi[N * N], ecnt = 1;
int p[N * N], vis[N * N];
void add(int u, int v) {
e[++ ecnt] = {fi[u], v, 0};
fi[u] = ecnt;
e[++ ecnt] = {fi[v], u, 0};
fi[v] = ecnt;
}
int gp(int x, int y) {
return (x - 1) * n + y;
}
int push(int u, int o) {
if(p[u] == 2 && o) return 1;
if(p[u] == 1 && ! o) return 1;
vis[u] = 1;
for(int i = fi[u]; i; i = e[i].ne) {
int v = e[i].to;
if(vis[v]) continue;
if(o && e[i].ew == 1) continue;
if(! o && e[i].ew != - 1) continue;
if(push(v, o)) {
e[i].ew ++;
e[i ^ 1].ew --;
return 1;
}
}
return 0;
}
void solve() {
cin >> n;
FOR(i, 1, n) FOR(j, 1, n) cin >> a[i][j];
FOR(i, 1, n) FOR(j, 1, n) if(a[i][j] >= 0) {
if(i < n && a[i + 1][j] >= 0)
add(gp(i, j), gp(i + 1, j));
if(j < n && a[i][j + 1] >= 0)
add(gp(i, j), gp(i, j + 1));
}
vector<PII> e;
FOR(i, 1, n) FOR(j, 1, n) if(a[i][j] > 0) {
p[gp(i, j)] = 2;
e.push_back({a[i][j], gp(i, j)});
}
sort(begin(e), end(e));
int flow = 0; ll ans = 0;
REP(i, SZ(e) - 1) {
int u = SE(e[i]);
memset(vis, 0, sizeof vis);
while(push(u, 0)) {
flow --;
memset(vis, 0, sizeof vis);
}
p[u] = 1;
memset(vis, 0, sizeof vis);
FOR(j, 0, i) {
int v = SE(e[j]);
while(push(v, 1)) {
flow ++;
memset(vis, 0, sizeof vis);
}
}
ans += 1ll * flow * (FI(e[i + 1]) - FI(e[i]));
}
cout << ans << endl;
}
ICPC 2024 Shanghai K - Knights of Night
首先一个点只需要保留其前 \(k\) 大的边,所以分别算出左右两边的点保留出来的边,然后取交。
接下来一条边只会禁掉 \(2k\) 条边,所以直接保留前 \(2k^2\) 大的边。
接下来跑二分图最大匹配即可。
ケロシの代码
namespace Mincost {
const int N = 2e5 + 5;
const int M = 1e6 + 5;
const int INF = 0x3f3f3f3f;
const ll LNF = 0x3f3f3f3f3f3f3f3f;
struct Edge { int ne, to, ew, ct; } e[M];
int fi[N], ecnt;
int n, S, T;
ll d[N];
int vis[N], f[N], p[N];
void init(int siz) {
ecnt = 1, n = siz;
FOR(i, 0, n) fi[i] = 0;
}
void add(int u, int v, int w, int c) {
e[++ ecnt] = {fi[u], v, w, c};
fi[u] = ecnt;
e[++ ecnt] = {fi[v], u, 0, - c};
fi[v] = ecnt;
}
bool spfa() {
FOR(i, 0, n) d[i] = LNF, vis[i] = 0;
queue<int> q; q.push(S);
d[S] = 0; f[S] = INF; vis[S] = 1;
while(! q.empty()) {
int u = q.front();
q.pop(); vis[u] = 0;
for(int i = fi[u]; i; i = e[i].ne) if(e[i].ew) {
int v = e[i].to;
if(d[v] > d[u] + e[i].ct) {
d[v] = d[u] + e[i].ct;
f[v] = min(f[u], e[i].ew);
p[v] = i;
if(! vis[v]) {
vis[v] = 1;
q.push(v);
}
}
}
}
return d[T] != LNF;
}
ll mincost(int _S, int _T) {
S = _S, T = _T;
if(! spfa()) return LNF;
int u = T;
while(u != S) {
int i = p[u];
e[i].ew -= f[T];
e[i ^ 1].ew += f[T];
u = e[i ^ 1].to;
}
return d[T];
}
}
const int N = 1e5 + 5;
const int P = 998244353;
const ll LNF = 0x3f3f3f3f3f3f3f3f;
int n, m, k, a[N], b[N], id[N], pre[N];
set<int> s[N];
struct Node {
int l, r, w;
bool operator < (const Node & A) const {
if(w == A.w) {
if(l == A.l) return r < A.r;
return l < A.l;
}
return w < A.w;
}
};
void solve() {
cin >> n >> m >> k;
FOR(i, 1, n) cin >> a[i];
FOR(i, 1, n) cin >> b[i];
REP(_, m) {
int u, v;
cin >> u >> v;
s[u].insert(v);
}
vector<Node> el, er;
FOR(i, 1, n) id[i] = i;
sort(id + 1, id + n + 1, [&] (int x, int y) {
return b[x] < b[y];
});
FOR(i, 2, n) {
if(b[id[i]] == b[id[i - 1]]) pre[i] = pre[i - 1];
else pre[i] = i - 1;
}
FOR(i, 1, n) {
int L = 1, R = n, pos = 0;
while(L <= R) {
int mid = L + R >> 1;
if(a[i] + b[id[mid]] < P) {
pos = mid;
L = mid + 1;
}
else {
R = mid - 1;
}
}
int cnt = 0;
REP(_, n) {
if(cnt == k) break;
if(pos == 0) pos = n;
if(! s[i].count(id[pos])) {
el.push_back({i, id[pos], (a[i] + b[id[pos]]) % P});
cnt ++;
pos --;
}
else {
pos = pre[pos];
}
}
}
sort(id + 1, id + n + 1, [&] (int x, int y) {
return a[x] < a[y];
});
FOR(i, 2, n) {
if(a[id[i]] == a[id[i - 1]]) pre[i] = pre[i - 1];
else pre[i] = i - 1;
}
FOR(i, 1, n) {
int L = 1, R = n, pos = 0;
while(L <= R) {
int mid = L + R >> 1;
if(b[i] + a[id[mid]] < P) {
pos = mid;
L = mid + 1;
}
else {
R = mid - 1;
}
}
int cnt = 0;
REP(_, n) {
if(cnt == k) break;
if(pos == 0) pos = n;
if(! s[id[pos]].count(i)) {
er.push_back({id[pos], i, (b[i] + a[id[pos]]) % P});
cnt ++;
pos --;
}
else {
pos = pre[pos];
}
}
}
sort(begin(el), end(el)); reverse(begin(el), end(el));
sort(begin(er), end(er)); reverse(begin(er), end(er));
vector<Node> E;
int lim = k * k * 2, pl = 0, pr = 0;
while(pl < SZ(el) && pr < SZ(er)) {
if(el[pl] < er[pr]) {
pr ++;
continue;
}
if(er[pr] < el[pl]) {
pl ++;
continue;
}
E.push_back(el[pl]);
pl ++; pr ++;
if(SZ(E) == lim) break;
}
map<int, int> L, R; int cl = 0, cr = 0;
for(auto h : E) {
if(! L.count(h.l)) L[h.l] = ++ cl;
if(! R.count(h.r)) R[h.r] = ++ cr;
}
Mincost :: init(cl + cr + 1);
int S = 0, T = cl + cr + 1;
FOR(i, 1, cl) Mincost :: add(S, i, 1, 0);
FOR(i, 1, cr) Mincost :: add(cl + i, T, 1, 0);
for(auto h : E) Mincost :: add(L[h.l], cl + R[h.r], 1, - h.w);
bool ok = 1; ll res = 0;
FOR(i, 1, k) {
ll val = Mincost :: mincost(S, T);
if(val == LNF) ok = 0;
else res -= val;
if(! ok) cout << - 1 << " ";
else cout << res << " ";
}
cout << endl;
}
AGC031 E - Snuke the Phantom Thief
考虑枚举拿的宝石个数后直接上下届费用流。
ケロシの代码
const int N = 2e2 + 5;
const int M = 1e3 + 5;
const int V = 100;
const int INF = 0x3f3f3f3f;
const ll LNF = 0x3f3f3f3f3f3f3f3f;
const ll LV = 2e15;
int n, m;
int ru[N];
int px[N], py[N]; ll w[N];
int F[N][2], G[N][2];
struct Edge { int ne, to, ew; ll ct; } e[M];
int fi[N], ecnt;
int S, T, ok;
ll d[N]; bool vis[N];
int f[N], p[N];
void init() {
ecnt = 1; ok = 1;
memset(fi, 0, sizeof fi);
memset(ru, 0, sizeof ru);
}
void add(int u, int v, int w, ll c) {
e[++ ecnt] = {fi[u], v, w, c};
fi[u] = ecnt;
e[++ ecnt] = {fi[v], u, 0, - c};
fi[v] = ecnt;
}
bool spfa() {
memset(d, 0x3f, sizeof d);
memset(vis, 0, sizeof vis);
queue<int> q; q.push(S);
d[S] = 0; vis[S] = 1; f[S] = INF;
while(! q.empty()) {
int u = q.front();
q.pop(); vis[u] = 0;
for(int i = fi[u]; i; i = e[i].ne) if(e[i].ew) {
int v = e[i].to;
if(d[v] > d[u] + e[i].ct) {
d[v] = d[u] + e[i].ct;
f[v] = min(f[u], e[i].ew);
p[v] = i;
if(! vis[v]) {
vis[v] = 1;
q.push(v);
}
}
}
}
return d[T] != LNF;
}
ll mincost() {
ll res = 0;
while(spfa()) {
res += d[T] * f[T];
int u = T;
while(u != S) {
int i = p[u];
e[i].ew -= f[T];
e[i ^ 1].ew += f[T];
u = e[i ^ 1].to;
}
}
return res;
}
void add_edge(int u, int v, int l, int r) {
if(l > r) ok = 0;
ru[u] -= l; ru[v] += l;
add(u, v, r - l, 0);
}
void solve() {
cin >> n;
FOR(i, 1, n) cin >> px[i] >> py[i] >> w[i];
FOR(i, 1, V) REP(j, 2) F[i][j] = G[i][j] = INF;
cin >> m;
REP(i, m) {
char o; int u, x;
cin >> o >> u >> x;
if(o == 'L') chmin(F[u][0], x);
if(o == 'R') chmin(F[u][1], x);
if(o == 'D') chmin(G[u][0], x);
if(o == 'U') chmin(G[u][1], x);
}
ROF(i, V - 1, 1) chmin(F[i][0], F[i + 1][0]);
ROF(i, V - 1, 1) chmin(G[i][0], G[i + 1][0]);
FOR(i, 2, V) chmin(F[i][1], F[i - 1][1]);
FOR(i, 2, V) chmin(G[i][1], G[i - 1][1]);
ll ans = 0;
S = V * 2 + 2, T = V * 2 + 3;
FOR(i, 1, n) {
init();
REP(j, V) add_edge(j, j + 1, i - F[j][0], F[j + 1][1]);
REP(j, V) add_edge(j + V + 2, j + V + 1, i - G[j][0], G[j + 1][1]);
FOR(j, 1, n) add(px[j], py[j] + V + 1, 1, - w[j]);
REP(j, V * 2 + 2) {
if(ru[j] > 0) add(S, j, ru[j], 0);
if(ru[j] < 0) add(j, T, - ru[j], 0);
}
add(V + 1, 0, INF, LV);
if(! ok) continue;
ll val = mincost();
for(int j = fi[S]; j; j = e[j].ne) if(e[j].ew) ok = 0;
for(int j = fi[T]; j; j = e[j].ne) if(e[j ^ 1].ew) ok = 0;
if(! ok) continue;
chmax(ans, LV * i - val);
}
cout << ans << endl;
}
[Ynoi Easy Round 2018] 星野瑠美衣
(这东西是不是应该放 Ynoi 做题笔记里(恼
考虑绝对值难搞,但是只要枚举所有的正负情况最后取个 max 即可。
这样的话两边点的贡献就可以独立,然后考虑建费用流。
接下来不难发现建出来的图是二分图,且一边只有 \(11\) 个点,所以直接模拟费用流,使用可删堆维护增广路即可。
ケロシの代码
const int N = 1e5 + 5;
const int INF = 0x3f3f3f3f;
template < typename T >
struct Set {
priority_queue<T> p, q;
Set() {
p.push({- INF, 0});
}
void insert(T x) {
p.push(x);
}
void del(T x) {
q.push(x);
}
void upd() {
while(! p.empty() && ! q.empty() && p.top() == q.top()) {
p.pop();
q.pop();
}
}
T top() {
upd();
return p.top();
}
};
int n, m;
struct Point {
int x, y;
} a[N], b[N];
int w[N], c[N], f[N][9], g[N][9];
int pl[N], pr[N];
Set<PII> q[11][11];
int S, T;
PII e[11][11];
int d[11], vis[11], pre[11];
void spfa() {
REP(i, 11) d[i] = - INF;
REP(i, 11) vis[i] = 0;
queue<int> q; q.push(S);
d[S] = 0; vis[S] = 1;
while(! q.empty()) {
int u = q.front();
q.pop(); vis[u] = 0;
REP(v, 11) if(u != v) {
int w = FI(e[u][v]);
if(d[v] < d[u] + w) {
d[v] = d[u] + w;
pre[v] = u;
if(! vis[v]) {
vis[v] = 1;
q.push(v);
}
}
}
}
}
void solve() {
cin >> n >> m;
FOR(i, 1, n) cin >> a[i].x >> a[i].y;
FOR(i, 1, m) cin >> b[i].x >> b[i].y >> w[i];
FOR(i, 1, n) {
int j = i % n + 1;
c[i] = abs(a[i].x - a[j].x) + abs(a[i].y - a[j].y);
vector<int> x {
abs(a[i].x - a[j].x),
a[i].x + a[j].x,
- a[i].x - a[j].x
};
vector<int> y {
abs(a[i].y - a[j].y),
a[i].y + a[j].y,
- a[i].y - a[j].y
};
REP(j, 9) f[i][j] = x[j / 3] + y[j % 3];
}
FOR(i, 1, m) {
vector<int> x {
0,
- b[i].x * 2,
b[i].x * 2
};
vector<int> y {
0,
- b[i].y * 2,
b[i].y * 2
};
REP(j, 9) g[i][j] = x[j / 3] + y[j % 3];
}
S = 9, T = 10;
ll res = 0;
FOR(i, 1, n) res += c[i];
FOR(i, 1, m) REP(j, 9) q[S][j].insert({g[i][j] + w[i], i});
FOR(i, 1, n) REP(j, 9) q[j][T].insert({f[i][j] - c[i], i + m});
REP(_, n) {
REP(i, 11) REP(j, 11) e[i][j] = q[i][j].top();
spfa();
res += d[T];
int u = T;
while(u != S) {
int v = pre[u];
int i = SE(e[v][u]);
if(v == S) {
REP(j, 9) q[S][j].del({g[i][j] + w[i], i});
pl[i] = u;
REP(j, 9) if(j != pl[i]) q[pl[i]][j].insert({g[i][j] - g[i][pl[i]], i});
}
else if(u == T) {
i -= m;
REP(j, 9) q[j][T].del({f[i][j] - c[i], i + m});
pr[i] = v;
REP(j, 9) if(j != pr[i]) q[j][pr[i]].insert({f[i][j] - f[i][pr[i]], i + m});
}
else if(i <= m) {
REP(j, 9) if(j != pl[i]) q[pl[i]][j].del({g[i][j] - g[i][pl[i]], i});
pl[i] = u;
REP(j, 9) if(j != pl[i]) q[pl[i]][j].insert({g[i][j] - g[i][pl[i]], i});
}
else {
i -= m;
REP(j, 9) if(j != pr[i]) q[j][pr[i]].del({f[i][j] - f[i][pr[i]], i + m});
pr[i] = v;
REP(j, 9) if(j != pr[i]) q[j][pr[i]].insert({f[i][j] - f[i][pr[i]], i + m});
}
u = v;
}
cout << res << " ";
}
cout << endl;
}
KAIST 2019 J - Jealous Teachers
神秘题。
性质一:学生的一个子集 \(S\) 相邻的老师个数一定大于 \(|S|\)
性质二:前 \(n-1\) 个学生和前 \(n-1\) 的老师存在完美匹配。
不难发现只需使一个学生向自己匹配的老师和另一个老师发花,且形成了一个树状物。
ケロシの代码
const int N = 2e5 + 5;
const int M = 1e6 + 5;
const int INF = 0x3f3f3f3f;
int n, m, p[N], b[N];
vector<int> g[N], t[N];
struct Edge { int ne, to, ew; } e[M];
int fi[N], ecnt;
int S, T, d[N], c[N];
set<int> F[N];
int fu[N], fv[N];
map<PII, int> ans;
int dp[N];
void init() {
ecnt = 1;
memset(fi, 0, sizeof fi);
}
void add(int u, int v, int w) {
e[++ ecnt] = {fi[u], v, w};
fi[u] = ecnt;
e[++ ecnt] = {fi[v], u, 0};
fi[v] = ecnt;
}
bool bfs() {
memset(d, 0x3f, sizeof d);
queue<int> q;
d[S] = 0; q.push(S);
while(! q.empty()) {
int u = q.front();
q.pop();
for(int i = fi[u]; i; i = e[i].ne) if(e[i].ew) {
int v = e[i].to;
if(d[v] == INF) {
d[v] = d[u] + 1;
q.push(v);
}
}
}
return d[T] != INF;
}
int dfs(int u, int w) {
if(u == T || ! w) return w;
int res = 0;
for(int & i = c[u]; i; i = e[i].ne) if(e[i].ew) {
int v = e[i].to;
if(d[v] != d[u] + 1) continue;
int val = dfs(v, min(w, e[i].ew));
if(! val) continue;
e[i].ew -= val;
e[i ^ 1].ew += val;
w -= val;
res += val;
if(! w) return res;
}
return res;
}
int dinic(int _S, int _T) {
S = _S, T = _T;
int res = 0;
while(bfs()) {
memcpy(c, fi, sizeof c);
res += dfs(S, INF);
}
return res;
}
void dfs(int u) {
int res = n - 1;
for(int v : t[u]) {
dfs(v);
res -= dp[v];
ans[{b[v], u}] = dp[v];
}
ans[{b[u], u}] = res;
dp[u] = n - res;
}
void solve() {
cin >> n >> m;
init();
int S = 0, T = n * 2;
FOR(i, 1, n - 1) add(S, i, 1);
FOR(i, 1, n - 1) add(i + n - 1, T, 1);
FOR(i, 1, m) {
int u, v;
cin >> u >> v;
fu[i] = u; fv[i] = v;
g[u].push_back(v);
if(u < n && v < n) add(u, v + n - 1, 1);
}
if(dinic(S, T) != n - 1) {
cout << - 1 << endl;
return;
}
FOR(u, 1, n - 1) {
for(int i = fi[u]; i; i = e[i].ne) if(e[i ^ 1].ew) {
int v = e[i].to;
if(v == S) continue;
p[u] = v - n + 1;
break;
}
}
FOR(i, 1, n - 1) b[p[i]] = i;
FOR(u, 1, n - 1) for(int v : g[u]) if(v != p[u]) F[v].insert(u);
queue<int> q;
q.push(n);
REP(_, n - 1) {
int pos = - 1;
while(! q.empty()) {
int u = q.front();
if(F[u].empty()) {
q.pop();
continue;
}
pos = * F[u].begin();
for(int v : g[pos]) if(v != p[pos]) F[v].erase(pos);
break;
}
if(pos == - 1) {
cout << - 1 << endl;
return;
}
t[q.front()].push_back(p[pos]);
q.push(p[pos]);
}
dfs(n);
FOR(i, 1, m) cout << ans[{fu[i], fv[i]}] << endl;
}

浙公网安备 33010602011771号