当前位置: 代码迷 >> 综合 >> 【JSOI2009】bzoj1449 球队收益
  详细解决方案

【JSOI2009】bzoj1449 球队收益

热度:31   发布时间:2024-01-13 11:02:49.0

Description Input Output 一个整数表示联盟里所有球队收益之和的最小值。

首先假设全输,然后给每场比赛分配一个赢家,每个队伍每多赢一场多获得的收益作为费用。
但是有一个问题,如何保证每次走的是对应的边?也就是,如何保证赢第一场的时候增加的收益是赢一场减赢零场,而不是赢两场减赢一场?可以发现,假设当前赢了 x 场输了 y 场,多赢一场的收益是 2cix?2diy+ci+di x 越大, y 越小,上式越大。因此每次一定优先走 x <script type="math/tex" id="MathJax-Element-215">x</script>比较小的边。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int s=6005,t=6006,oo=0x3f3f3f3f,mod=6007;
int fir[6010],ne[200010],to[200010],w[200010],c[200010],
que[6010],in[6010],minw[6010],len[6010],fa[6010],
cnt[5010],ci[5010],di[5010],win[5010],lose[5010],
n,m,num,ans;
void add(int u,int v,int x,int y)
{num++;ne[num*2]=fir[u];fir[u]=num*2;to[num*2]=v;w[num*2]=x;c[num*2]=y;ne[num*2+1]=fir[v];fir[v]=num*2+1;to[num*2+1]=u;w[num*2+1]=0;c[num*2+1]=-y;
}
int main()
{int hd,tl,u,v;scanf("%d%d",&n,&m);for (int i=1;i<=n;i++) scanf("%d%d%d%d",&win[i],&lose[i],&ci[i],&di[i]);for (int i=1;i<=m;i++){scanf("%d%d",&u,&v);cnt[u]++;cnt[v]++;add(s,i+n,1,0);add(i+n,u,1,0);add(i+n,v,1,0);}for (int i=1;i<=n;i++)for (int j=1;j<=cnt[i];j++)add(i,t,1,ci[i]*(win[i]+j)*(win[i]+j)+di[i]*(lose[i]+cnt[i]-j)*(lose[i]+cnt[i]-j)-ci[i]*(win[i]+j-1)*(win[i]+j-1)-di[i]*(lose[i]+cnt[i]-j+1)*(lose[i]+cnt[i]-j+1));for (int i=1;i<=n;i++) ans+=ci[i]*win[i]*win[i]+di[i]*(lose[i]+cnt[i])*(lose[i]+cnt[i]);while (1){hd=0,tl=1;que[0]=s;in[s]=1;memset(minw,0,sizeof(minw));minw[s]=oo;memset(len,0x3f,sizeof(len));len[s]=0;while (hd!=tl){u=que[hd++];hd%=mod;for (int i=fir[u];i;i=ne[i])if (w[i]&&len[v=to[i]]>len[u]+c[i]){len[v]=len[u]+c[i];minw[v]=min(minw[u],w[i]);fa[v]=i;if (!in[v]){in[v]=1;que[tl++]=v;tl%=mod;}}in[u]=0;}if (!minw[t]) break;ans+=minw[t]*len[t];for (int i=fa[t];i;i=fa[to[i^1]]){w[i]-=minw[t];w[i^1]+=minw[t];}}printf("%d\n",ans);
}