19 hdu多校 Harmonious Army //划分两类建最小割

http://acm.hdu.edu.cn/showproblem.php?pid=6598

题意:

给n个人,每个人有2种职业可以选择,给m个关系,对于每一个关系,如果人x和人y属于第一种职业,power增加A,如果人x和人y属于第二种职业,power增加C,如果人x和人y不属于同一种职业,power增加B=A/3+C/4,求每个人选择职业后的最大power。

思路:

最小割割开后,每个顶点确切的属于s或t

s点连接顶点,代表连接的顶点是一种职业,t点连接顶点,代表连接的顶点是另外一种职业。

考虑一个(u,v,A,B,C) 

连接方式总贡献为W=A+B+C。总贡献-最小割,使得答案最大。

枚举每一种切割方法,建立方程。

切割a,b,相当于总贡献减掉的(A+B),那么就令a+b=A+B 

切割c,d,相当于总贡献减掉的(B+C),令c+d=B+C

切割a,e,d,相当于总贡献减掉(A+C),令a+e+d=A+C

切割b,e,c,相当于总贡献减掉(A+C),令b+e+c=A+C

可得一组解:e=(A+C)/2-B,a=b=(A+B)/2,c=d=(B+C)/2。

(题目给的 b=a/4+c/3 保证e>0,保证没有负环)

这样就使得W-(a+b)=C    W-(c+d)=A   W-(a+e+d)=B   W-(b+e+c)=B。

因为是最小割,就保证了减去的最小,那么减去的最小,答案就最大。

考虑到多个  (u,v,A,B,C) 按照上述建图

对于同一个顶点(可能连s和t各有多条边),因为是最小割,割了连s的一边的某条边,那么连t的所有边都不会割,就保证了每个点确切的属于s或者t, 这样保证了合理性,就能拓展到多个 (u,v,A,B,C)。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define int long long
#define FI first
#define SE second
#define MP make_pair
#define PII pair<int,int>
const int MXLOG = 30;
const LL mod = 998244353;
const int MX = 1e6+5;
const int max_n = 505;
struct no{int to,cap,rev;}; //arc
vector<no>g[max_n]; //图
int level[max_n]; //到起点的距离
int iter[max_n]; //当前弧,在其之前的边已经没用了
void addarc(int s,int e,int c){
	g[s].push_back((no){e,c,g[e].size()});
	g[e].push_back((no){s,0,g[s].size()-1});
}
//更新层次,即level
void bfs(int s){
	memset(level,-1,sizeof(level));
	level[s]=0;
	queue<int>q;
	q.push(s);
	while(!q.empty()){
		int now=q.front();q.pop();
		for(int i=0;i<(int)g[now].size();i++){
			no &arc=g[now][i];
			if(level[arc.to]!=-1||arc.cap<=0) continue;
			level[arc.to]=level[now]+1;
			q.push(arc.to);
		}
	}
}
//寻找增广路
int dfs(int v,int t,int f){
	if(v==t) return f;
	for(iter[v];iter[v]<(int)g[v].size();iter[v]++){
		no &arc=g[v][iter[v]];
		if(arc.cap<=0||level[arc.to]!=level[v]+1) continue;
		int d=dfs(arc.to,t,min(f,arc.cap));
		if(d>0) {
			arc.cap=arc.cap-d;
			g[arc.to][arc.rev].cap+=d;
			return d;
		}
	}
	return 0;
}
int Dinic(int s,int t){
	int re=0;
	while(1){
		bfs(s);
		memset(iter,0,sizeof(iter));
		if(level[t]==-1) return re;
		int f;
		while((f=dfs(s,t,LLONG_MAX))>0)
			re=re+f;
	}
	return re;
}

signed main(){
    int n,m;
    while(cin>>n>>m){
        for(int i=0;i<=n;i++)g[i].clear();
        LL ans=0;
        for(int i=1;i<=m;i++){
            int uu,vv,aa,bb,cc;scanf("%I64d%I64d%I64d%I64d%I64d",&uu,&vv,&aa,&bb,&cc);
            ans=ans+(LL)aa+(LL)bb+(LL)cc;
            addarc(0,uu,aa+bb);
            addarc(0,vv,aa+bb);
            addarc(uu,n+1,bb+cc);
            addarc(vv,n+1,bb+cc);
            addarc(uu,vv,aa+cc-bb*2);
            addarc(vv,uu,aa+cc-bb*2);
        }
        ans=ans*2-Dinic(0,n+1);
        cout<<ans/2<<'\n';
    }
    return 0;
}

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值