首页 > 代码库 > 【bzoj1449/bzoj2895】[JSOI2009]球队收益/球队预算 费用流

【bzoj1449/bzoj2895】[JSOI2009]球队收益/球队预算 费用流

题目描述

技术分享

输入

技术分享

输出

一个整数表示联盟里所有球队收益之和的最小值。

样例输入

3 3
1 0 2 1
1 1 10 1
0 1 3 3
1 2
2 3
3 1

样例输出

43


题解

费用流

由于存在一个赢一个输,比较难算。我们可以先假设它们都输掉,然后再安排赢的情况。

设fi为i还要打的比赛数目,那么初始的收益为∑ci*wi^2+di*(li+fi)^2。

S->每场比赛,容量为1,费用为0。

每场比赛->比赛的两队,容量为1,费用为0。

因为费用的改变是包含平方的,所以我们需要拆边来做。

第i支队伍向T连fi条边,容量均为1,第j条边表示赢j场比赢j-1场多出来的收益,所以费用应为ci*(wi+j)^2+di*(wi+fi-j)^2-ci*(li+j-1)^2-di*(li+j+1)^2。

这里为了方便,直接把fi加到了li中。

然后跑最小费用最大流,加上之前的初始收益即为答案。

#include <cstdio>#include <cstring>#include <queue>#define N 10010#define M 3500000using namespace std;queue<int> q;int w[N] , l[N] , c[N] , d[N] , x[N] , y[N] , f[N];int head[N] , to[M] , val[M] , cost[M] , next[M] , cnt = 1 , s , t , dis[N] , from[N] , pre[N];void add(int x , int y , int v , int c){	to[++cnt] = y , val[cnt] = v , cost[cnt] = c , next[cnt] = head[x] , head[x] = cnt;	to[++cnt] = x , val[cnt] = 0 , cost[cnt] = -c , next[cnt] = head[y] , head[y] = cnt;}bool spfa(){	int x , i;	memset(from , -1 , sizeof(from));	memset(dis , 0x3f , sizeof(dis));	dis[s] = 0 , q.push(s);	while(!q.empty())	{		x = q.front() , q.pop();		for(i = head[x] ; i ; i = next[i])			if(val[i] && dis[to[i]] > dis[x] + cost[i])				dis[to[i]] = dis[x] + cost[i] , from[to[i]] = x , pre[to[i]] = i , q.push(to[i]);	}	return ~from[t];}int mincost(){	int i , k , ans = 0;	while(spfa())	{		k = 0x7fffffff;		for(i = t ; i != s ; i = from[i]) k = min(k , val[pre[i]]);		ans += k * dis[t];		for(i = t ; i != s ; i = from[i]) val[pre[i]] -= k , val[pre[i] ^ 1] += k;	}	return ans;}int main(){	int n , m , i , j , ans = 0;	scanf("%d%d" , &n , &m) , s = 0 , t = m + n + 1;	for(i = 1 ; i <= n ; i ++ ) scanf("%d%d%d%d" , &w[i] , &l[i] , &c[i] , &d[i]);	for(i = 1 ; i <= m ; i ++ ) scanf("%d%d" , &x[i] , &y[i]) , f[x[i]] ++ , f[y[i]] ++ , l[x[i]] ++ , l[y[i]] ++ ;	for(i = 1 ; i <= n ; i ++ ) ans += c[i] * w[i] * w[i] + d[i] * l[i] * l[i];	for(i = 1 ; i <= m ; i ++ ) add(s , i , 1 , 0) , add(i , x[i] + m , 1 , 0) , add(i , y[i] + m , 1 , 0);	for(i = 1 ; i <= n ; i ++ )		for(j = 1 ; j <= f[i] ; j ++ )			add(i + m , t , 1 , c[i] * (2 * w[i] + 2 * j - 1) - d[i] * (2 * l[i] - 2 * j + 1));	printf("%d\n" , ans + mincost());	return 0;}

 

 

【bzoj1449/bzoj2895】[JSOI2009]球队收益/球队预算 费用流