Description
Input
Output
一个整数表示联盟里所有球队收益之和的最小值。
Sample Input
3 3
1 0 2 1
1 1 10 1
0 1 3 3
1 2
2 3
3 1
1 0 2 1
1 1 10 1
0 1 3 3
1 2
2 3
3 1
Sample Output
43
HINT
题解:
直接算支出不好计算。
可以先假设所有队伍一开始都输了.
考虑每个队伍赢一场会增加多少支出.
c*(x+1)^2-d*(y-1)^2-c*x^2-d*y^2=2*c*x-2*d*y+c+d;
所以我们可以从源点向每场比赛连容量为1费用为0的边。
每场比赛向这场比赛的两支队伍连容量为1费用为0的边。
然后统计一下每支队伍参加的比赛数。
每支队伍向汇点连这支队伍的比赛数条边.
容量都为1.费用依次为当前状态再多赢1场增加的支出.
初始答案加上最小费用流即可。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 7000
#define M 200000
#define inf 2100000000
using namespace std;
int point[N],next[M<<1],T,n,m,cnt(1),win[N],los[N];
int dis[N],pre[N],f[N],q[N*100],ans,c[N],d[N],x,y,p[N];
struct use{int st,en,v,c;}e[M<<1];
void add(int x,int y,int v,int c){
next[++cnt]=point[x];point[x]=cnt;
e[cnt].st=x;e[cnt].en=y;e[cnt].v=v;e[cnt].c=c;
next[++cnt]=point[y];point[y]=cnt;
e[cnt].st=y;e[cnt].en=x;e[cnt].v=0;e[cnt].c=-c;
}
bool spfa(){
for (int i=1;i<=T;i++) dis[i]=inf;
memset(f,0,sizeof(f));
int h(0),t(1);q[t]=1;f[1]=1;dis[1]=0;
while (h<t){
int u=q[++h];f[u]=0;
for (int i=point[u];i;i=next[i])
if (e[i].v&&(dis[e[i].en]>dis[u]+e[i].c)){
dis[e[i].en]=dis[u]+e[i].c;
pre[e[i].en]=i;
if (!f[e[i].en]){
f[e[i].en]=1;
q[++t]=e[i].en;
}
}
}
return dis[T]!=inf;
}
void aug(){
int mn=inf;
for (int i=T;i!=1;i=e[pre[i]].st) mn=min(mn,e[pre[i]].v);
for (int i=T;i!=1;i=e[pre[i]].st){
e[pre[i]].v-=mn;e[pre[i]^1].v+=mn;ans+=mn*e[pre[i]].c;
}
}
int main(){
//freopen("a.in","r",stdin);
// freopen("b.out","w",stdout);
scanf("%d%d",&n,&m);T=n+m+2;
for (int i=1;i<=m;i++) add(1,i+1,1,0);
for (int i=1;i<=n;i++)
scanf("%d%d%d%d",&win[i],&los[i],&c[i],&d[i]);
for (int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
add(i+1,x+m+1,1,0);
add(i+1,y+m+1,1,0);
p[x]++;p[y]++;
}
for (int i=1;i<=n;i++) los[i]+=p[i];
for (int i=1;i<=n;i++)
ans+=c[i]*win[i]*win[i]+d[i]*los[i]*los[i];
for (int i=1;i<=n;i++)
for (int j=1;j<=p[i];j++){
add(i+m+1,T,1,2*c[i]*win[i]-2*d[i]*los[i]+c[i]+d[i]);
win[i]++;los[i]--;
}
while (spfa()) aug();
cout<<ans<<endl;
}