bzoj1449 [JSOI2009]球队收益

1449: [JSOI2009]球队收益

Time Limit: 5 Sec  Memory Limit: 64 MB
Submit: 1131  Solved: 640
[Submit][Status][Discuss]

Description

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

分析:很妙的idea.

   首先要能想到用最小费用最大流求解. 为什么呢?把比赛当作点,每一场比赛有两种结果,那么就可以认为容量为1. 要使得收益最小,加上费用,就是求最小费用最大流了.但是确定不了每场比赛的收益. 

   一个非常巧妙的做法:

   我们考虑费用的增量:多赢一场比赛产生的收益。
   即(C(w + 1)^2 + D(l − 1)^2 ) − (Cw^2 + Dl^2 ) = 2wC − 2lD + C + D。
   对于第i支队伍,假设后m场中i参加的有x场,那么最初w = win,
   l = lose + x,之后每赢一场w + +,l − −。我们从第i支队伍的点向汇连x条边,
   分别代表第i支队伍赢了j场比赛时相对赢j − 1场时收益的增量。由于增量一定
   越来越大(平方嘛),所以流量最先流过的一定是费用较小的边,即j最小的边。
   答案即所有队伍最初收益+最小费用最大流的费用。

   费用递增模型可以采用拆边的方式跑费用流. 先确定最初状态,保证以后的费用都是递增的,这是算法正确性的保证!

#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 10010,inf = 0x7fffffff;
int n,m,win[maxn],lose[maxn],c[maxn],d[maxn];
int a[maxn],b[maxn],ans,S,T,vis[maxn],vis2[maxn];
int head[maxn],to[maxn * 6],nextt[maxn * 6],w[maxn * 6],cost[maxn * 6],tot = 2;

void add(int x,int y,int z,int p)
{
    w[tot] = z;
    cost[tot] = p;
    to[tot] = y;
    nextt[tot] = head[x];
    head[x] = tot++;

    w[tot] = 0;
    cost[tot] = -p;
    to[tot] = x;
    nextt[tot] = head[y];
    head[y] = tot++;
}

bool spfa()
{
    queue <int> q;
    memset(vis,0,sizeof(vis));
    memset(vis2,0,sizeof(vis2));
    for (int i = 1; i <= T; i++)
        d[i] = inf;
    d[S] = 0;
    vis[S] = 1;
    q.push(S);
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for (int i = head[u];i;i = nextt[i])
        {
            int v = to[i];
            if (w[i] && d[v] > d[u] + cost[i])
            {
                d[v] = d[u] + cost[i];
                if (!vis[v])
                {
                    vis[v] = 1;
                    q.push(v);
                }
            }
        }
    }
    return d[T] < inf;
}

int dfs(int u,int f)
{
    if (u == T)
    {
        ans += f * d[u];
        return f;
    }
    int res = 0;
    vis2[u] = 1;
    for (int i = head[u];i;i = nextt[i])
    {
        int v = to[i];
        if (w[i] && !vis2[v] && d[v] == d[u] + cost[i])
        {
            int temp = dfs(v,min(f - res,w[i]));
            w[i] -= temp;
            w[i ^ 1] += temp;
            res += temp;
            if (res == f)
                return res;
        }
    }
    return res;
}

void dinic()
{
    while(spfa())
        dfs(S,inf);
}

int main()
{
    scanf("%d%d",&n,&m);
    S = n + m + 1;
    T = S + 1;
    for (int i = 1; i <= n; i++)
        scanf("%d%d%d%d",&win[i],&lose[i],&c[i],&d[i]);
    for (int i = 1; i <= m; i++)
    {
        scanf("%d%d",&a[i],&b[i]);
        lose[a[i]]++;
        lose[b[i]]++;
    }
    for (int i = 1; i <= n; i++)
        ans += win[i] * win[i] * c[i] + lose[i] * lose[i] * d[i];
    for (int i = 1; i <= m; i++)
    {
        add(S,i,1,0);
        add(i,a[i] + m,1,0);
        add(i,b[i] + m,1,0);
        add(a[i] + m,T,1,c[a[i]] * (2 * win[a[i]] + 1) - d[a[i]] * (2 * lose[a[i]] - 1));
        win[a[i]]++;
        lose[a[i]]--;
        add(b[i] + m,T,1,c[b[i]] * (2 * win[b[i]] + 1) - d[b[i]] * (2 * lose[b[i]] - 1));
        win[b[i]]++;
        lose[b[i]]--;
    }
    dinic();
    printf("%d\n",ans);

    return 0;
}

 

 

posted @ 2018-03-18 15:24  zbtrs  阅读(242)  评论(0编辑  收藏  举报