2021ccpc威海H city safety(最小割,最大权闭合子图)

题意为给我们一棵树

修复每个点需要费用

当一个点周围i个点都被修复时,就会获得a[i]的收入,如果多个满足取最大值

问最大收入为多少

首先根据最大权闭合图模型

先把所有的收益全部加上去。之后计算最小割,使得对于每份收益,要么放弃要么支付对应的费用

答案转化为总收益-最小割


我们看n = 200非常小,直接floyd暴力一遍所有点的算法,矩阵存起来

然后开始建图,我们定义V_{i,p}为对于i这个点,小于等于p距离的点全选

1.源点连向每个V_{i,p},容量为V_{i,p}-V_{i,p-1} 代表增量收益

2,每个V_{i,p}连接对应与i点距离为p的原图中的点,容量为INF

3,每个原图中的点连向 汇点,表示支付修复这个点的费用

4,每个V_{i,p}连向V_{i,p-1}来限制割边,必须先放弃p才能放弃p+1

建出来的图大概长这样

 


答案等于总收益减去最小割。

代码如下

#include <bits/stdc++.h>
#define int long long
#define pb push_back
#define fer(i,a,b) for(int i=a;i<=b;++i)
#define der(i,a,b) for(int i=a;i>=b;--i)
#define all(x) (x).begin(),(x).end()
#define pll pair<int,int>
#define et  cout<<'\n'
#define xx first
#define yy second
using namespace std;
template <typename _Tp>void input(_Tp &x){
    char ch(getchar());bool f(false);while(!isdigit(ch))f|=ch==45,ch=getchar();
    x=ch&15,ch=getchar();while(isdigit(ch))x=x*10+(ch&15),ch=getchar();
    if(f)x=-x;
}
template <typename _Tp,typename... Args>void input(_Tp &t,Args &...args){input(t);input(args...);}
 
int id[255][255], id1[255], dis[255][255], w[255], v[255];
int n;
const int N = 1e6+10, M = N*2,INF = 1e9+7;
int S, T;
int h[N], e[M], f[M], ne[M], idx;
int d[N], cur[N];
void add(int a, int b, int c)
{
    e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
    e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx ++ ;
}
bool bfs()
{
    queue<int> q;
    memset(d, -1, sizeof d);
    q.push(S);
    d[S] = 0, cur[S] = h[S];
    while (q.size())
    {
        auto t=q.front();
        q.pop();
        for (int i = h[t]; ~i; i = ne[i])
        {
            int ver = e[i];
            if (d[ver] == -1 && f[i])
            {
                d[ver] = d[t] + 1;
                cur[ver] = h[ver];
                if (ver == T)  return true;
                q.push(ver);
            }
        }
    }
    return false;
}
int find(int u, int limit)
{
    if (u == T) return limit;
    int flow = 0;
    for (int i = cur[u]; ~i && flow < limit; i = ne[i])
    {
        cur[u] = i;  // 当前弧优化
        int ver = e[i];
        if (d[ver] == d[u] + 1 && f[i])
        {
            int t = find(ver, min(f[i], limit - flow));
            if (!t) d[ver] = -1;
            f[i] -= t, f[i ^ 1] += t, flow += t;
        }
    }
    return flow;
}
int dinic()
{
    int r = 0, flow;
    while (bfs()) while (flow = find(S, INF)) r += flow;
    return r;
}
signed main(){
    memset(h,-1,sizeof h);
    S=0;
    input(n);
    int cnt = 0, ans;
    fer(i,1,n){
        fer(j,1,n){
            dis[i][j] = 0x3f3f3f3f;
        }
    }
    fer(i,1,n){
        input(w[i]);
        dis[i][i] = 0;
    }
    fer(i,0,n-1){
        input(v[i]);
    } 
    ans = n*v[n - 1];
    fer(i,1,n-1){
        int u, v;
        input(u,v);
        dis[v][u]=dis[u][v]=1;
    }
    fer(k,1,n){
        fer(i,1,n){
            fer(j,1,n){
                dis[i][j]=min(dis[i][j], dis[i][k] + dis[k][j]);
            }
        }
    }
    fer(i,1,n) id1[i]=++cnt;
    fer(i,1,n){
        fer(j,0,n-1){
            id[i][j]=++cnt;
        }
    }
    T=++cnt;
    fer(i,1,n){
        fer(j,0,n-1){
            if(j==0){
                add(S,id[i][j],v[j]);
            }
            else{
                add(S,id[i][j],v[j]-v[j-1]);
            }
            if (j) {
                add(id[i][j], id[i][j - 1],INF);
            }
        }
    }
    fer(i,1,n){
        add(id1[i],T,w[i]);
    }
    fer(i,1,n){
        fer(j,1,n){
            int d=dis[i][j];
            add(id[i][d],id1[j],INF);
        }
    }
    cout << ans - dinic();
    return 0;
}


 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值