【bzoj1449】【JSOI2009】【球队收益】【费用流】

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

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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值