0
点赞
收藏
分享

微信扫一扫

2021 CCPC 威海 city safety(最小割建图)

googlefrank 2022-04-14 阅读 37
算法

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,jvi,j1连接一条容量为无限大的边,把源点和每个 v i , j v_{i,j} vi,j连接容量为 v [ j ] − v [ j − 1 ] v[j]-v[j-1] v[j]v[j1]的边,割掉源点的边代表放弃这个收益。
③对于每个 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;
}

举报

相关推荐

0 条评论