magic

magic

magic

题目描述

 

给定一个n个点,m条边的有向图。

对于任意一个点i,都有两个权值ai,bi。你可以花费bi的费用将这个点的ai变成0。

另外对于圈中的每个点你需要付出wi=Max(i,j)∈E aj

 

 

请最小化所有费用之和。

 

 

输入

 

第一行两个数n,m。

接下来一行n个数,表示ai。

接下来一行n个数,表示bi。

接下来m行,每行2个数i,j , 表示一行(i,j)的边

 

 

输出

 

输出一个数,表示最小化的费用

 

 

样例输入

2 1
100000 10000
100000 1
1 2

样例输出

1

提示

 

【样例说明】最优的方案是花费1的费用将a2变成0

【数据范围】
对于30%的数据,n<=20
对于100%的数据,n<=1000,m<=50000

 

solution

一个神奇的建图方法

对于原图中的每一个点,建out[i]个点(out[i]表示i的出度)

这out[i]个点和T连边,流量b[i]

把这out[i]个点按a[i]排序

差分(保证代价正确)

然后再把这out个点连inf的边(一起选)

 

如图

 

嗯有点丑

意会一下

然后最小割即可

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#define maxn 200005
#define inf 1e9
using namespace std;
int n,m,a1[1002],b1[1002],u1[50002],v1[50002],tot=1;
int head[maxn],cnt,S,T,d[maxn],flag[maxn],top,ans,cur[maxn];
vector<int>G[maxn];
queue<int>q;
struct node{
    int id,v;
}t[maxn];
struct no{
    int v,nex,cap;
}e[400005];
bool cmp(node aa,node bb){
    return aa.v<bb.v;
}
void add(int t1,int t2,int t3){
    e[++tot].v=t2;e[tot].cap=t3;e[tot].nex=head[t1];head[t1]=tot;
}
void lj(int t1,int t2,int t3){
    add(t1,t2,t3);add(t2,t1,0);
}
bool BFS(){
     
    for(int i=1;i<=T;i++)d[i]=inf;
    d[S]=0;q.push(S);
    while(!q.empty()){
        int x=q.front();q.pop();
        cur[x]=head[x];
        for(int i=head[x];i;i=e[i].nex){
            //cout<<e[i].v<<' '<<x<<endl;
            if(d[e[i].v]>d[x]+1&&e[i].cap>0){
                d[e[i].v]=d[x]+1;
                if(!flag[e[i].v]){
                    flag[e[i].v]=1;q.push(e[i].v);
                }
            }
        }
        flag[x]=0;
    }
    return d[T]!=inf;
}
int lian(int k,int a){
    if(k==T||!a)return a;
    int f,flow=0;
    for(int &i=cur[k];i;i=e[i].nex){
        if(d[e[i].v]==d[k]+1&&(f=lian(e[i].v,min(e[i].cap,a)))>0){
            e[i].cap-=f;e[i^1].cap+=f;
            a-=f;flow+=f;
            if(!a)break;
        }
    }
    return flow;
}
int main()
{
    cin>>n>>m;
    S=200002,T=S+1;cnt=n;
    for(int i=1;i<=n;i++)scanf("%d",&a1[i]);
    for(int i=1;i<=n;i++){
        scanf("%d",&b1[i]);
        lj(i,T,b1[i]);
    }
    for(int i=1;i<=m;i++){
        scanf("%d%d",&u1[i],&v1[i]);
        G[u1[i]].push_back(v1[i]);
    }
    for(int i=1;i<=n;i++){
        int sz=G[i].size();
        for(int j=0;j<sz;j++){
            t[j].id=G[i][j],t[j].v=a1[G[i][j]];
        }
        sort(t,t+sz,cmp);
        for(int i=0;i<sz;i++){
            int now=++cnt;
            if(i==0)lj(S,now,t[i].v);
            else {
                lj(S,now,t[i].v-t[i-1].v);
                lj(now-1,now,inf);
            }
            lj(now,t[i].id,inf);
        }
    }
    while(BFS())ans+=lian(S,inf);
    cout<<ans<<endl;
    return 0;
}
 

 

posted @ 2018-08-12 15:46 liankewei 阅读( ...) 评论( ...) 编辑 收藏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值