[BZOJ1449][JSOI2009]球队收益

bzoj
luogu

Description

在一个篮球联赛里,有\(n\)支球队,球队的支出是和他们的胜负场次有关系的,具体来说,第i支球队的赛季总支出是\(C_i\times x^2+D_i \times y^2,D_i \le C_i\)(赢得多,给球员的奖金就多嘛), 其中\(x,y\)分别表示这只球队本赛季的胜负场次。现在赛季进行到了一半,每只球队分别取得了\(a_i\)场胜利和\(b_i\)场失利。而接下来还有\(m\)场比赛要进行。问联盟球队的最小总支出是多少。

sol

费用流建模。
先假设剩下的每场比赛双方都输了,这样可以算出一个基础支出。
因为每场比赛要有一个赢家,所以对于一场比赛,我们需要让参赛双方中的一只球队的胜场++,负场--。
对于\(i\)号球队,每有一场比赛由输变为赢,设其当前的胜负场数分别为\(x,y\),则\(\Delta\)支出=\(C_i(x+1)^2+D_i(y-1)^2-C_ix^2-D_iy^2=C_i(2x+1)+D_i(-2y+1)\)
可以看出,每有一场比赛由输变为赢,其\(\Delta\)支出的不一样的。
而且是。。。递增的?
所以拆边就行了。在跑费用流的时候一定会选择费用更小的边所以答案一定是对的。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 6005;
struct edge{int to,nxt,w,cost;}a[N<<2];
int n,m,S,T,A[N],B[N],C[N],D[N],du[N],head[N],cnt=1;
int dis[N],vis[N],pe[N],ans;queue<int>Q;
void link(int u,int v,int w,int cost){
    a[++cnt]=(edge){v,head[u],w,cost};head[u]=cnt;
    a[++cnt]=(edge){u,head[v],0,-cost};head[v]=cnt;
}
bool spfa(){
    memset(dis,63,sizeof(dis));
    dis[S]=0;Q.push(S);
    while (!Q.empty()){
        int u=Q.front();Q.pop();
        for (int e=head[u];e;e=a[e].nxt){
            int v=a[e].to;
            if (a[e].w&&dis[v]>dis[u]+a[e].cost){
                dis[v]=dis[u]+a[e].cost;pe[v]=e;
                if (!vis[v]) vis[v]=1,Q.push(v);
            }
        }
        vis[u]=0;
    }
    if (dis[T]==dis[0]) return false;
    ans+=dis[T];
    for (int i=T;i!=S;i=a[pe[i]^1].to)
        --a[pe[i]].w,++a[pe[i]^1].w;
    return true;
}
int main(){
    n=gi();m=gi();S=n+m+1;T=S+1;
    for (int i=1;i<=n;++i)
        A[i]=gi(),B[i]=gi(),C[i]=gi(),D[i]=gi();
    for (int i=1;i<=m;++i){
        int u=gi(),v=gi();
        link(S,n+i,1,0);link(n+i,u,1,0);link(n+i,v,1,0);
        ++du[u];++du[v];
    }
    for (int i=1;i<=n;++i){
        B[i]+=du[i];ans+=C[i]*A[i]*A[i]+D[i]*B[i]*B[i];
        for (int j=1;j<=du[i];++j)
            link(i,T,1,C[i]*(2*A[i]+1)+D[i]*(-2*B[i]+1)),++A[i],--B[i];
    }
    while (spfa()) ;
    printf("%d\n",ans);return 0;
}

猜你喜欢

转载自www.cnblogs.com/zhoushuyu/p/9097812.html