bzoj 1449: [JSOI2009]球队收益 (费用流)

来源:互联网 发布:北京java测试薪资 编辑:程序博客网 时间:2024/05/28 05:17

题目描述

传送门

题目大意:在一个篮球联赛里,有n支球队,球队的支出是和他们的胜负场次有关系的,具体来说,第i支球队的赛季总支出是Cix2+Diy2Di<=Ci。其中x,y分别表示这只球队本赛季的胜负场次。现在赛季进行到了一半,每只球队分别取得了win[i]场胜利和lost[i]场失利。而接下来还有m场比赛要进行。问联盟球队的最小总支出是多少。

题解

对于每支队伍,计算出还有打dui场比赛,然后初始的时候我们假设所有队伍的比赛状态都是输,即win[i]=win[i],lost[i]=lost[i]+du[i]
然后考虑每赢一场的对总支出的影响,c[i](win[i]+1)2c[i]win[i]2+d[i](lost[i]1)2d[i]lost[i]2
化简后得到c[i]+d[i]+2win[i]c[i]2lost[i]d[i]
对于每支队伍来说,剩多少场比赛,就从这支队伍代表的点向汇点连几条边,每条边的容量为1,费用为上式的答案,每连完一条边,win[i]++,lost[i],重新计算边权。
每场比赛必有一支队伍获胜,所有源点向每场比赛代表的点连边,容量为1,费用为0
每场比赛向进行的两支队伍连边,容量为1,费用为0.
然后跑费用流即可。
注意最后的答案要加入初值ni=1lost[i]lost[i]d[i]+win[i]win[i]c[i]

代码

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#include<queue>#define N 300010#define inf 1000000000using namespace std;int n,tot,c[N],d[N],cost[N],remain[N],point[N],nxt[N],v[N],ctx[N];int cty[N],du[N],dx[N],dy[N],dis[N],can[N],last[N],ans,m,lost[N],win[N],S,T,cnt[N];void add(int x,int y,int z,int k){    tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; remain[tot]=z; cost[tot]=k;    tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; remain[tot]=0; cost[tot]=-k;}int addflow(int s,int t){    int now=t; int ans=inf;    while (now!=s) {        ans=min(ans,remain[last[now]]);        now=v[last[now]^1];    }    now=t;    while (now!=s){        remain[last[now]]-=ans;        remain[last[now]^1]+=ans;        now=v[last[now]^1];    }    return ans;}bool spfa(int s,int t){    for (int i=1;i<=t;i++) dis[i]=inf,can[i]=0;    queue<int> p; p.push(s); dis[s]=0; can[s]=1;    while (!p.empty()){        int now=p.front(); p.pop();        for (int i=point[now];i!=-1;i=nxt[i])         if (remain[i]&&dis[v[i]]>dis[now]+cost[i]){            dis[v[i]]=dis[now]+cost[i];            last[v[i]]=i;            if(!can[v[i]]) {                can[v[i]]=1;                p.push(v[i]);             }         }        can[now]=0;    }    if (dis[t]==inf) return false;     int flow=addflow(s,t);    ans+=flow*dis[t];    return true;}void solve(int s,int t){    while(spfa(s,t));}int main(){    freopen("a.in","r",stdin);//  freopen("my.out","w",stdout);    scanf("%d%d",&n,&m);    tot=-1;    memset(point,-1,sizeof(point));    for (int i=1;i<=n;i++) scanf("%d%d%d%d",&win[i],&lost[i],&c[i],&d[i]);    S=n+m+1; T=S+1;    for (int i=1;i<=m;i++) {        int x,y; scanf("%d%d",&x,&y);        add(S,i,1,0); dx[i]=x; dy[i]=y;        du[x]++; du[y]++;        add(i,x+m,1,0); ctx[i]=tot;        add(i,y+m,1,0); cty[i]=tot;     }    for (int i=1;i<=n;i++) lost[i]+=du[i];    for (int i=1;i<=n;i++) ans+=c[i]*win[i]*win[i]+d[i]*lost[i]*lost[i];    for (int i=1;i<=n;i++)      for (int j=1;j<=du[i];j++){      int t=win[i]*2*c[i]+c[i]+d[i]-lost[i]*2*d[i];      add(i+m,T,1,t);      win[i]++; lost[i]--;    }    solve(S,T);     printf("%d\n",ans);}
0 0
原创粉丝点击