2021CCPC威海H题city safety (最小割模型 or 树形dp)
题解
官方题解里已经很详细地讲了建立最小割模型,再跑网络流的做法。时间复杂度为\(O(能过)\)
主要说一下树形dp的做法,这题可以发现一个性质:如果\(i\)节点能获得\(v_p\)的收益,那么\(i\)的邻点获得的收益一定属于\(\{v_{p},v_{p + 1}, v_{p - 1}\}\)。
进一步可以发现,如果\(i\)取\(v_p\)的收益时,它的所有邻点可以随意从\(\{v_{p},v_{p + 1}, v_{p - 1}\}\)中取值,互相不会冲突。
于是可以设置状态:\(dp[i][j]\)表示以\(i\)为根节点的子树根节点取得\(v_j\)收益时,整棵子树的最大收益。
于是很容易得到转移方程:
\[dp[i][j] += max\{dp[son_i][j], dp[son_i][j - 1], dp[son_i][j + 1]\}
\]
dp代码
/*************************************************************************
> File Name: 1.cpp
> Author: Knowledge_llz
> Mail: 925538513@qq.com
> Blog: https://www.cnblogs.com/Knowledge-Pig/
************************************************************************/
#include<bits/stdc++.h>
#define LL long long
#define endl '\n'
using namespace std;
const int maxx = 2e5 + 10;
int n, w[maxx], v[maxx], dp[250][250];
vector<int> vec[maxx];
void dfs(int id, int fa){
for(int i = 1; i <= n; ++i) dp[id][i] = v[i] - w[id];
for(auto u : vec[id]){
if(u == fa) continue;
dfs(u, id);
for(int i = 1; i < n; ++i)
dp[id][i] += max({dp[u][i - 1], dp[u][i], dp[u][i + 1]});
dp[id][0] += max(dp[u][0], dp[u][1]);
dp[id][n] += max(dp[u][n], dp[u][n - 1]);
}
}
int main(){
ios::sync_with_stdio(false); cin.tie(0);
#ifndef ONLINE_JUDGE
freopen("input.in", "r", stdin);
freopen("output.out","w", stdout);
#endif
cin >> n;
for(int i = 1; i <= n; ++i) cin >> w[i];
for(int i = 1; i <= n; ++i) cin >> v[i];
for(int i = 1, u, v; i < n; ++i){
cin >> u >> v;
vec[u].push_back(v);
vec[v].push_back(u);
}
dfs(1, 0);
int ans = 0;
for(int i = 0; i <= n; ++i) ans = max(ans, dp[1][i]);
cout << ans << endl;
return 0;
}
网络流代码
/*************************************************************************
> File Name: 1.cpp
> Author: Knowledge_llz
> Mail: 925538513@qq.com
> Blog: https://www.cnblogs.com/Knowledge-Pig/
************************************************************************/
#include<bits/stdc++.h>
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define LL long long
#define pb push_back
#define fi first
#define se second
#define pr pair<int,int>
#define mk(a,b) make_pair(a,b)
#define endl '\n'
using namespace std;
const int maxx = 2e5 + 10, inf = 1e9;
int n, be[maxx], ne[maxx * 10], to[maxx * 10], w[maxx * 10], dep[maxx], cost[maxx], v[maxx], now[maxx], e = 1;
int s = maxx - 10, t = maxx - 11, cnt;
vector<int> vec[250], dis[250][250];
void add(int x, int y, int z, bool p){
to[++e] = y;
ne[e] = be[x];
be[x] = e;
w[e] = z;
if(p) add(y, x, 0, 0);
}
void dfs(int rt, int id, int fa, int deep){
dis[rt][deep].push_back(id);
for(auto u : vec[id]) if(u != fa) dfs(rt, u, id, deep + 1);
}
bool bfs(){
queue<int> q;
dep[t] = 0;
for(int i = 1; i <= cnt; ++i) dep[i] = 0;
q.push(s);
dep[s] = 1; now[s] = be[s];
while(!q.empty()){
int id = q.front(); q.pop();
for(int i = be[id]; i; i = ne[i]){
int u = to[i];
if(!dep[u] && w[i] > 0){
dep[u] = dep[id] + 1;
now[u] = be[u];
q.push(u);
}
}
}
return dep[t] > 0;
}
int dfs(int id, int mini){
if(id == t || !mini) return mini;
int flow, ret = 0;
for(int &i = now[id]; i; i = ne[i]){
int go = to[i];
if(w[i] > 0 && dep[go] == dep[id] + 1){
flow = dfs(go, min(mini, w[i]));
if(!flow) dep[go] = inf;
w[i] -= flow;
w[i ^ 1] += flow;
ret += flow;
mini -= flow;
}
if(!mini) return ret;
}
return ret;
}
int dinic(){
int ans = 0, tmp;
while(bfs()){
tmp = dfs(s, inf);
if(!tmp) break;
ans += tmp;
}
return ans;
}
int main(){
ios::sync_with_stdio(false); cin.tie(0);
#ifndef ONLINE_JUDGE
freopen("input.in", "r", stdin);
freopen("output.out","w", stdout);
#endif
cin >> n;
for(int i = 1; i <= n; ++i){ cin >> cost[i]; add(i, t, cost[i], 1); }
for(int i = 1; i <= n; ++i) cin >> v[i];
for(int i = 1, u, v; i < n; ++i){
cin >> u >> v;
vec[u].push_back(v);
vec[v].push_back(u);
}
for(int i = 1; i <= n; ++i) dfs(i, i, 0, 1);
cnt = n; int ans = v[n] * n;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j){
++cnt;
add(s, cnt, v[j] - v[j - 1], 1);
if(j > 1) add(cnt, cnt - 1, inf, 1);
for(auto u : dis[i][j]) add(cnt, u, inf, 1);
}
cout << ans - dinic() << endl;
return 0;
}