[BZOJ1449][JSOI2009]球队收益
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;
}