www.cnblogs.com/shaokele/
bzoj1449: [JSOI2009]球队收益##
Time Limit: 5 Sec
Memory Limit: 64 MBDescription###
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
题目地址: bzoj1449: [JSOI2009]球队收益
题目大意: 题目很简洁了:)####
题解: hzwer
最小费用最大流
首先假设每场比赛两队都是负......算出初始收益
然后考虑每次比赛,其中一队为胜
从源向每场比赛连流量1费用0的边,从比赛向这场比赛的两支队都连一条流量1费用0的边
然后考虑费用的增量:多赢一场比赛产生的收益。即 $$(C(w+1)2+D*(l-1)2)-(Cw2+D*l2)=2wC-2lD+C+D$$
对于某支球队,假设后M场中其参加x场,那么最初 (w=win,l=lose+x) ,之后每赢一场 (w++,l--)。
我们从它向汇连 (x) 条边,分别代表其赢了 (j) 场比赛时相对赢 (j-1) 场时收益的增量。由于增量递增,所以可以保证正确性。
答案为所有队伍最初收益+最小费用最大流费用。
AC代码
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=5005,M=1005,inf=1e9;
int n,m,S,T,cnt,ans;
int last[N+M],q[N+M],d[N+M],from[N+M];
int win[N],lose[N],C[N],D[N],s[N];
bool inq[N+M];
struct edge{
int from,to,v,c,next;
}e[100005];
void insert(int u,int v,int w,int c){
e[++cnt]=(edge){u,v,w,c,last[u]};last[u]=cnt;
e[++cnt]=(edge){v,u,0,-c,last[v]};last[v]=cnt;
}
bool spfa(){
for(int i=S;i<=T;i++)d[i]=inf;
int l=0,r=1;q[0]=S;
d[S]=0;inq[S]=1;
while(l!=r){
int u=q[l++];if(l==T)l=0;
for(int i=last[u];i;i=e[i].next)
if(e[i].v && d[e[i].to]>d[u]+e[i].c){
d[e[i].to]=d[u]+e[i].c;
from[e[i].to]=i;
if(!inq[e[i].to]){
inq[e[i].to]=1;
q[r++]=e[i].to;
if(r==T)r=0;
}
}
inq[u]=0;
}
if(d[T]==inf)return 0;
return 1;
}
void mcf(){
int x=inf;
for(int i=from[T];i;i=from[e[i].from])
x=min(x,e[i].v);
for(int i=from[T];i;i=from[e[i].from]){
e[i].v-=x;
e[i^1].v+=x;
ans+=e[i].c*x;
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d%d%d%d",&win[i],&lose[i],&C[i],&D[i]);
S=0;T=n+m+1;cnt=1;
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
insert(S,i,1,0);
insert(i,m+u,1,0);
insert(i,m+v,1,0);
s[u]++;s[v]++;
}
for(int i=1;i<=n;i++){
lose[i]+=s[i];
ans+=win[i]*win[i]*C[i]+lose[i]*lose[i]*D[i];
}
for(int i=1;i<=n;i++)
for(int j=1;j<=s[i];j++){
insert(m+i,T,1,2*C[i]*win[i]+C[i]+D[i]-2*D[i]*lose[i]);
lose[i]--;win[i]++;
}
while(spfa())mcf();
printf("%d
",ans);
return 0;
}