【BZOJ1449】球队收益(JSOI2009)-费用流+拆边

测试地址:球队收益
做法:本题需要用到费用流+拆边。
由于每场比赛只能有一个胜者,不难想到从每场比赛向比赛双方的队伍各连一条容量为1的边,并从源点向每场比赛连一条容量为1的边,这样每个队伍获得的流量就是它赢的场次了。我们先假设一支队伍所涉及的所有比赛它都输了,这种情况下会获得Cixi2+Di(yi+ti)2的收益,其中ti为这支队伍参加的场次,xi,yi就是它已经赢/输的场次。那么如果赢了a场,它就会获得比原来多Ci(2axia2)Di(2a(yi+ti)a2)的收益。我们发现这个很难表现成一条边的费用,于是我们把这条边拆成ti条容量为1的边,并使得它在选其中费用最少的a条边时,费用和为上面那个式子,我们可以构造出第a条边的费用应该为Ci(2(xi+a)1)Di(2(yi+tia)+1),即从赢a1场到赢a场新增的收益,显然这个东西是随着a的增大而增大的,所以满足上面的条件。那么我们把图建出来后,跑一遍最小费用最大流就可以求出最小的新增收益,最后加上原来我们假设已经获得的收益即可。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf=1000000000000000000ll;
int n,m,S,T,first[6010]={0},tot=1,last[6010],laste[6010];
ll x[5010],y[5010],c[5010],d[5010],deg[5010]={0};
ll dis[6010],sum=0;
bool vis[6010];
queue<int> Q;
struct edge
{
    int v,next;
    ll f,c;
}e[30010];

void insert(int a,int b,ll f,ll c)
{
    e[++tot].v=b,e[tot].next=first[a],e[tot].f=f,e[tot].c=c,first[a]=tot;
    e[++tot].v=a,e[tot].next=first[b],e[tot].f=0,e[tot].c=-c,first[b]=tot;
}

void init()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%lld%lld%lld%lld",&x[i],&y[i],&c[i],&d[i]);
    S=n+m+1,T=n+m+2;
    for(int i=1;i<=m;i++)
    {
        int a,b;
        insert(S,i,1,0);
        scanf("%d%d",&a,&b);
        deg[a]++,deg[b]++;
        insert(i,m+a,1,0);
        insert(i,m+b,1,0);
    }
    for(int i=1;i<=n;i++)
    {
        sum+=c[i]*x[i]*x[i]+d[i]*(y[i]+deg[i])*(y[i]+deg[i]);
        for(ll j=1;j<=deg[i];j++)
            insert(m+i,T,1,c[i]*(2ll*(x[i]+j)-1ll)-d[i]*(2ll*(y[i]+deg[i]-j)+1));
    }
}

bool spfa()
{
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=T;i++)
        dis[i]=inf;
    dis[S]=0;
    vis[S]=1;
    Q.push(S);
    while(!Q.empty())
    {
        int v=Q.front();Q.pop();
        for(int i=first[v];i;i=e[i].next)
            if (e[i].f&&dis[e[i].v]>dis[v]+e[i].c)
            {
                last[e[i].v]=v;
                laste[e[i].v]=i;
                dis[e[i].v]=dis[v]+e[i].c;
                if (!vis[e[i].v]) Q.push(e[i].v),vis[e[i].v]=1;
            }
        vis[v]=0;
    }
    return dis[T]!=inf;
}

void mincost()
{
    ll minc=0;
    while(spfa())
    {
        int x=T;
        ll maxf=inf;
        while(x!=S)
        {
            maxf=min(maxf,e[laste[x]].f);
            x=last[x];
        }
        x=T;
        while(x!=S)
        {
            e[laste[x]].f-=maxf;
            e[laste[x]^1].f+=maxf;
            x=last[x];
        }
        minc+=maxf*dis[T];
    }
    printf("%lld",sum+minc);
}

int main()
{
    init();
    mincost();

    return 0;
}
posted @ 2018-04-27 11:33  Maxwei_wzj  阅读(84)  评论(0编辑  收藏  举报