【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 3500000 using 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; }