【JSOI2009】球队收益

费用流

题目大意:

有 $n$ 支球队,第 $i$ 支球队目前已经赢了 $a_i$ 场,输了 $b_i$ 场。最终,赢 $x$ 场,输 $y$ 场,球队要支出 $C_i\times x^2+D_i\times y^2$ 元($C_i\geq D_i$)。

现在还有 $m$ 场比赛,每场比赛在两个队之间进行,且必定分出胜负。

问,在所有可能的胜负情况中,所有球队的总支出最少可能是多少。

题解:

考虑网络流。

对每支球队建点,对每场比赛建点。比赛点向比赛对应的队伍连容量 $1$,费用 $0$ 的边,源点向比赛点建容量 $1$,费用 $0$ 的边。这样处理完了胜负的决策。

但是一支球队不论胜负,都有收益,而且收益和胜负的场数的平方成正比。

平方收益可以拆贡献:$x^2=1+3+5+\dots+(2x-1)$,即赢第 $k$ 场的收益为 $C_i\times(2k-1)$,输第 $k$ 场的收益为 $D_i\times(2k-1)$。

考虑先默认所有球队都输光了。对于 $1$ 的流量,我们把它看成将一次输球转化为赢球,那么多出的贡献就是再赢一场球的收益减去最后输的那场球的收益。

我们要使总收益最小,跑最小费用最大流即可。由于使得费用最小,上面拆出的贡献肯定会先走贡献小的边,所以计算是正确的。

Code:

#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int N=6813;
int a[N],b[N],C[N],D[N],head[N],cnt=1,n,m,T,dis[N],ans,iter[N];
int dg[N];
bool vis[N];
queue<int>q;
struct edge{
    int to,nxt,cap,cost;
}e[2000000];
inline void addedge(int u,int v,int cap,int cost){
    e[++cnt]=(edge){v,head[u],cap,cost},head[u]=cnt;
    e[++cnt]=(edge){u,head[v],0,-cost},head[v]=cnt;
}
int dfs(int u,int f){
    if(!f||u==T)return f;
    vis[u]=1;
    int ret=0;
    for(int&i=iter[u];i;i=e[i].nxt)
    if(e[i].cap&&!vis[e[i].to]&&dis[e[i].to]==dis[u]+e[i].cost){
        int d=dfs(e[i].to,min(f,e[i].cap));
        e[i].cap-=d,e[i^1].cap+=d,ret+=d,f-=d;
        if(!f)break;
    }
    vis[u]=0;
    return ret;
}
bool MCMF(){
    memset(dis,0x3f,(T+1)<<2);
    dis[0]=0;
    for(q.push(0);!q.empty();){
        int u=q.front();q.pop();
        vis[u]=0;
        for(int i=head[u];i;i=e[i].nxt)
        if(e[i].cap&&dis[e[i].to]>dis[u]+e[i].cost){
            dis[e[i].to]=dis[u]+e[i].cost;
            if(!vis[e[i].to])vis[e[i].to]=1,q.push(e[i].to);
        }
    }
    if(dis[T]>1e9)return 0;
    memcpy(iter,head,(T+1)<<2);
    ans+=dis[T]*dfs(0,0x3f3f3f3f);
    return 1;
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n>>m;
    T=n+m+1;
    for(int i=1;i<=n;++i)cin>>a[i]>>b[i]>>C[i]>>D[i];
    for(int i=n+1;i<=n+m;++i){
        addedge(0,i,1,0);
        int s,t;
        cin>>s>>t;
        addedge(i,s,1,0),addedge(i,t,1,0);
        ++dg[s],++dg[t];
    }
    for(int i=1;i<=n;++i){
        ans+=a[i]*a[i]*C[i]+(b[i]+dg[i])*(b[i]+dg[i])*D[i];
        for(int l=2*a[i]+1,r=2*(b[i]+dg[i])-1;l<=2*(a[i]+dg[i])-1;l+=2,r-=2)
        addedge(i,T,1,l*C[i]-r*D[i]);
    }
    while(MCMF());
    cout<<ans<<'\n';
    return 0;
}