版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
题目
题意
你有 个士兵,每个士兵需要安排一个职位(法师或战士),另给定 对士兵,对于其中的一对 ,如果都选战士,战队的攻击力会提高 点( );如果都选法师,攻击力提高 点( );如果一个战士一个法师,攻击力提高 点( ),问战队攻击力最多提升多少点。
分析
这类“二元分配,组合加成”的问题,一般可以用总和(即所有
、
、
之和)减最小割完成,建图如下:
我们的目的是使不同割法对应不同选法的损失。
图中的
、
是两个有加成关系士兵,割
表示
选战士,割
表示
选法师;割
表示
选战士,割
表示
选法师。于是有这几种情况:
- 割 和 ,则损失的战斗力是 ,所以 ;
- 割 和 ,则损失的战斗力是 ,所以 ;
- 割 和 或者割 和 ,这时由于 的存在,图还是联通的,所以必须再割 ,所以 。
注意你可能觉得还有其他割法,但是,由于边权必须是正数(最大流最小割不能跑负容量),所以显然割集 一定比 优( ),所以只有上述几种情况。
联立得到(不定)方程组:
其中
、
、
是常数。
找到一组解:
于是按这个建边即可,注意先乘
使边权为整数。
(题目中
的奇怪的表达式应该只是让
吧= =)
代码
#include<queue>
#include<cstdio>
#include<algorithm>
using namespace std;
int read(){
int x=0;bool f=0;char c=getchar();
while(c<'0'||c>'9') f|=c=='-',c=getchar();
while(c>='0'&&c<='9') x=x*10+(c^48),c=getchar();
return f?-x:x;
}
int N,M;
#define MAXN 50000
#define MAXM 1000000
#define INF int(1e9)
#define LL long long
struct Edge{
int v,w,Next;
}E[MAXM+5];
int Adj[MAXN+5],ecnt;
void AddEdge(int u,int v,int w){
E[ecnt].v=v,E[ecnt].w=w,E[ecnt].Next=Adj[u],Adj[u]=ecnt++;
E[ecnt].v=u,E[ecnt].w=0,E[ecnt].Next=Adj[v],Adj[v]=ecnt++;
}
int Cur[MAXN+5];
int Dist[MAXN+5];
bool bfs(int T){
for(int i=0;i<=T;i++)
Cur[i]=Adj[i],Dist[i]=-1;
queue<int> Q;
Q.push(0),Dist[0]=0;
while(!Q.empty()){
int u=Q.front();Q.pop();
for(int i=Adj[u];i!=-1;i=E[i].Next){
int v=E[i].v;
if(E[i].w&&Dist[v]==-1){
Dist[v]=Dist[u]+1;
Q.push(v);
}
}
}
return Dist[T]>=0;
}
int dfs(int u,int T,int Min){
if(!Min||u==T)
return Min;
int Rest=Min,Now;
for(int &i=Cur[u];i!=-1;i=E[i].Next){
int v=E[i].v,w=E[i].w;
if(Dist[v]==Dist[u]+1&&(Now=dfs(v,T,min(Rest,w)))){
Rest-=Now,
E[i].w-=Now,
E[i^1].w+=Now;
if(!Rest)
break;
}
}
return Min-Rest;
}
LL Dinic(int T){
LL ret=0;
while(bfs(T))
ret+=dfs(0,T,INF);
return ret;
}
int main(){
while(~scanf("%d%d",&N,&M)){
ecnt=0;
for(int i=0;i<=N+1;i++)
Adj[i]=-1;
LL Sum=0;
for(int i=1;i<=M;i++){
int u=read(),v=read(),A=read()*2,B=read()*2,C=read()*2;
Sum+=A+B+C;
AddEdge(0,u,(A+B)/2),AddEdge(0,v,(A+B)/2);
AddEdge(u,N+1,(B+C)/2),AddEdge(v,N+1,(B+C)/2);
AddEdge(u,v,(A+C)/2-B),AddEdge(v,u,(A+C)/2-B);
}
printf("%lld\n",(Sum-Dinic(N+1))/2);
}
return 0;
}