2021 CCPC 威海 city safety(最小割建图)
链接
题意:给出一颗树,每个点有修复他需要的权值,以及对于都是一样的
v
[
i
]
v[i]
v[i]代表一个点如果距离为
i
i
i的所有点都被修复这个点可以获得的价值是多少,问最后最多可以获得多少价值。
心路历程:虽然一下感觉树形dp应该可以写,但是不会,看了题解开始往最小割开始想,有了大概思路建图错了很久,一直wa42,最后发现是自己以为的可以整合的边整合会出问题。。对着其他题解改了一下然后过了。
思路:这个题要求的是最大花费,并且有限制,可以类比杭电最小割例题,把所有结果加入答案中,然后建出一个符合的图,求出最小割即可。
①对于每个点有自己的
w
[
i
]
w[i]
w[i],把他们向汇点连一条容量为
w
[
i
]
w[i]
w[i]的边,割掉这条边代表花费
w
[
i
]
w[i]
w[i]的代价修复这个城市。
②定义状态
v
i
,
j
v_{i,j}
vi,j代表离
i
i
i的距离为
j
j
j,我们把每个
v
i
,
j
→
v
i
,
j
−
1
v_{i,j}\to v_{i,j-1}
vi,j→vi,j−1连接一条容量为无限大的边,把源点和每个
v
i
,
j
v_{i,j}
vi,j连接容量为
v
[
j
]
−
v
[
j
−
1
]
v[j]-v[j-1]
v[j]−v[j−1]的边,割掉源点的边代表放弃这个收益。
③对于每个
v
i
,
j
v_{i,j}
vi,j连接所有与
i
i
i距离为
j
j
j的点,容量为无限大,配合②中连边方式,限制每个点在放弃时只能从大到小放弃。因为根据最小割最大流定理,为了达到最大流量,每个
v
i
,
j
v_{i,j}
vi,j也必须先流距离为
j
j
j的点,然后才能向下流。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int id[255][255], id1[255], f[255][255], w[255], v[255];
typedef long long ll;
const ll INF = 2147483647000000;
const int N = 90005, M = 10000010;
struct Edge{ll to, nxt, d;}e[M];
ll head[N], idx = 1;
void add(ll u, ll v, ll d) {e[++idx].to = v, e[idx].nxt = head[u], e[idx].d = d, head[u] = idx;}
ll dep[N], gap[N], cur[N];
ll n, m, s, t;
ll maxflow;
void bfs()
{
memset(dep, -1, sizeof(dep));
memset(gap, 0, sizeof (gap));
dep[t] = 0;
gap[0] = 1;
queue<ll> Q;
Q.push(t);
while (!Q.empty()) {
ll u = Q.front(); Q.pop();
for (ll i = head[u]; i; i = e[i].nxt) {
ll v = e[i].to;
if (dep[v] != -1) continue;
dep[v] = dep[u] + 1;
gap[dep[v]]++;
Q.push(v);
}
}
}
ll dfs(ll u, ll flow)
{
if (u == t) {
maxflow += flow;
return flow;
}
ll used = 0;
for (ll& i = cur[u]; i; i = e[i].nxt) {
ll v = e[i].to, d = e[i].d;
if (d && dep[v] + 1 == dep[u]) {
ll mi = dfs(v, min(flow - used, d));
if(mi) {
e[i].d -= mi;
e[i ^ 1].d += mi;
used += mi;
}
if (used == flow) return used;
}
}
gap[dep[u]]--;
if (gap[dep[u]] == 0) dep[s] = N + 1;
dep[u]++;
gap[dep[u]]++;
return used;
}
ll ISAP()
{
maxflow = 0;
bfs();
while (dep[s] < N) {memcpy(cur,head,sizeof(head));dfs(s, INF);}
return maxflow;
}
signed main()
{
cin >> n;
int cnt = 0, ans;
for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) f[i][j] = 0x3f3f3f3f;
for (int i = 1; i <= n; i++) {cin >> w[i]; f[i][i] = 0;}
for (int i = 0; i < n; i++) {cin >> v[i];}
ans = n * v[n - 1];
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
f[v][u] = f[u][v] = 1;
}
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
f[i][j] = min(f[i][j], f[i][k] + f[k][j]);
}
}
}
for (int i = 1; i <= n; i++) id1[i] = ++cnt;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < n; j++) {
id[i][j] = ++cnt;
}
}
t = ++cnt;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < n; j++) {
add(s, id[i][j], j ? (v[j] - v[j - 1]) : v[j]); add(id[i][j], s, 0);
if (j) {
add(id[i][j], id[i][j - 1], INF); add(id[i][j - 1], id[i][j], 0);
}
}
}
for (int i = 1; i <= n; i++) {add(id1[i], t, w[i]); add(t, id1[i], 0);}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
int d = f[i][j];
add(id[i][d], id1[j], INF); add(id1[j], id[i][d], 0);
}
}
cout << ans - ISAP();
return 0;
}