UOJ Logo zhouzixuan的博客

博客

bzoj 1449: [JSOI2009]球队收益

2015-11-16 13:57:08 By zhouzixuan
【题目描述】:

【题目解法】:    
    首先我们考虑lose[i]=sum[i]-win[i] sum[i]表示i队总共进行的比赛数量
    这样我们答案的式子就变成:(ci+di)*win[i]^2+sum[i]*sum[i]*d[i]-2*d[i]*sum[i]*win[i]
    然后我们考虑将win[i]+1后贡献的式子为:(c[i]+d[i])*(2*win[i]+1)-2*d[i]*sum[i]
    然后我们考虑那个麻烦的win[i],也就是说贡献的式子和上一次的win[i]有关
    那么我们考虑拆点,把每个点拆成sum[i]个点,每个点表示上次为win[i]这次的贡献
    然后我们再对每个点设置一个阈值节点来控制流入每个点的边数
    这样建完图后就可以跑一边费用流即可
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=10005;
const int M=200005;
const int INF=32083208;
struct edge {int x,c,f,op,next;} b[M];
int n,m,tot,win[N],lose[N],sum[N],c[N],d[N],s,t;
int a[N],pre[N],cur[N],gap[N],dis[N],ans,q[M]; bool v[N];
inline long long getint()
{
    long long x=0; char c=getchar(); bool flag=false;
    while ((c!='-')&&((c<'0')||(c>'9'))) c=getchar();
    if (c=='-') flag=true,c=getchar();
    while ((c>='0')&&(c<='9')) x=x*10+(long long)(c-'0'),c=getchar();
    if (flag) return -x; else return x;
}
inline void addedge(int x,int y,int c,int f)
{
    ++tot; b[tot].x=y; b[tot].c=c; b[tot].f=f; b[tot].op=tot+1; b[tot].next=a[x]; a[x]=tot;
    ++tot; b[tot].x=x; b[tot].c=0; b[tot].f=-f; b[tot].op=tot-1; b[tot].next=a[y]; a[y]=tot;
}
void init()
{
    n=getint(); m=getint(); s=0; t=n+m;
    for (int i=1; i<=n; i++)
    {
        win[i]=getint(); lose[i]=getint();
        c[i]=getint(); d[i]=getint();
    }
    for (int i=1; i<=m; i++)
    {
        int x=getint(),y=getint(); addedge(s,i,1,0);
        addedge(i,x+m,1,0); addedge(i,y+m,1,0);
        sum[x]++; sum[y]++; t+=2;
    }
    for (int i=1; i<=n; i++)
        for (int j=1; j<=sum[i]; j++)
        {
            int x=win[i]+j-1; t++;
            addedge(i+m,t,1,(c[i]+d[i])*2*x+c[i]+d[i]-2*d[i]*(sum[i]+lose[i]+win[i]));
        }
    for (int i=n+m+1; i<=t; i++) addedge(i,t+1,INF,0); t++;
    for (int i=1; i<=n; i++) sum[i]+=lose[i]+win[i];
    for (int i=1; i<=n; i++) ans+=c[i]*win[i]*win[i]+d[i]*(sum[i]-win[i])*(sum[i]-win[i]);
}
bool spfa()
{
    memset(dis,127,sizeof(dis)); memset(v,true,sizeof(v)); int inf=dis[0];
    int head=0,tail=1; q[1]=s; v[s]=false; pre[s]=0; dis[s]=0;
    while (head<tail)
    {
        head++; if (head>=M) head=1; int k=q[head]; v[k]=true;
        for (int p=a[k];p;p=b[p].next)
        {
            int pp=b[p].x; if ((dis[k]+b[p].f>=dis[pp])||(b[p].c<=0)) continue;
            dis[pp]=dis[k]+b[p].f; pre[pp]=k; cur[pp]=p; if (!v[pp]) continue;
            v[pp]=false; tail++; if (tail>=M) tail=1; q[tail]=pp;
        }
    }
    return dis[t]!=inf;
}
void adjust()
{
    for (int i=t; i!=s; i=pre[i]) b[cur[i]].c--,b[b[cur[i]].op].c++;
    ans+=dis[t];
}
void solve()
{
    while (spfa()) adjust();
    printf("%d\n",ans);
}
int main()
{
    init();
    solve();
    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。