• 【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;
    }
  • 相关阅读:
    java 用代码实现判断字符串的开头和结尾
    java基础 1-path
    C#基础(语句 for循环)
    C#基础(数组)
    C#基础(语句 if else)
    C#基础(变量、常量、运算符)
    继承-person
    继承-字母表
    继承-monkey
    继承-people
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793430.html
Copyright © 2020-2023  润新知