bzoj1449【JSOI2009】球队收益

148 篇文章 0 订阅
38 篇文章 0 订阅

1449: [JSOI2009]球队收益

Time Limit: 5 Sec   Memory Limit: 64 MB
Submit: 540   Solved: 298
[ 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


Source




费用流的应用

可以发现这道题与之前的费用流题有所不同,因为一个球队不论输还是赢都会获得收益。

这里就要用到一个技巧,我们一开始让比赛的双方都输,并且计算相应的收益,这样每场只要修改赢的那个人的收益就可以了。

然后就是每个人向终点连边,这里的费用随着流量的变化而变化,所以我们要用到拆边法。考虑一个人从赢win[i]次、输lose[i]次变为赢(win[i]+1)、输(lose[i]-1)次,总费用的变化量为c[i]*(win[i]+1)^2+d[i]*(lose[i]-1)^2-c[i]*win[i]^2-d[i]*lose[i]^2=c[i]*(2*win[i]+1)-d[i]*(2*lose[i]-1)。就这样一条一条地加边,同时修改win[i]lose[i]




#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<queue>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define pa pair<int,int>
#define maxn 6500
#define maxm 11000
#define inf 1000000000
using namespace std;
struct edge_type
{
	int from,to,next,v,c;
}e[maxm];
int cnt=1,ans=0,s,t,n,m;
int head[maxn],p[maxn],dis[maxn];
int win[maxn],lose[maxn],a[maxn],b[maxn],c[maxn],d[maxn];
bool inq[maxn];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void add_edge(int x,int y,int v,int c)
{
	e[++cnt]=(edge_type){x,y,head[x],v,c};head[x]=cnt;
	e[++cnt]=(edge_type){y,x,head[y],0,-c};head[y]=cnt;
}
inline bool spfa()
{
	queue<int>q;
	F(i,1,t) dis[i]=inf;
	memset(inq,false,sizeof(inq));
	dis[s]=0;inq[s]=true;q.push(s);
	while (!q.empty())
	{
		int x=q.front();inq[x]=false;q.pop();
		for(int i=head[x];i;i=e[i].next)
		{
			int y=e[i].to;
			if (e[i].v&&dis[y]>dis[x]+e[i].c)
			{
				dis[y]=dis[x]+e[i].c;
				p[y]=i;
				if (!inq[y]){inq[y]=true;q.push(y);}
			}
		}
	}
	return dis[t]!=inf;
}
inline void mcf()
{
	while (spfa())
	{
		int tmp=inf;
		for(int i=p[t];i;i=p[e[i].from]) tmp=min(tmp,e[i].v);
		ans+=dis[t]*tmp;
		for(int i=p[t];i;i=p[e[i].from]){e[i].v-=tmp;e[i^1].v+=tmp;}
	}
}
int main()
{
	n=read();m=read();
	s=n+m+1;t=s+1;
	F(i,1,n){win[i]=read();lose[i]=read();c[i]=read();d[i]=read();}
	F(i,1,m){a[i]=read();b[i]=read();lose[a[i]]++;lose[b[i]]++;}
	F(i,1,n) ans+=c[i]*win[i]*win[i]+d[i]*lose[i]*lose[i];
	F(i,1,m)
	{
		add_edge(s,i,1,0);add_edge(i,a[i]+m,1,0);add_edge(i,b[i]+m,1,0);
		int tmp=c[a[i]]*(2*win[a[i]]+1)-d[a[i]]*(2*lose[a[i]]-1);
		add_edge(a[i]+m,t,1,tmp);
		win[a[i]]++;lose[a[i]]--;
		tmp=c[b[i]]*(2*win[b[i]]+1)-d[b[i]]*(2*lose[b[i]]-1);
		add_edge(b[i]+m,t,1,tmp);
		win[b[i]]++;lose[b[i]]--;
	}
	mcf();
	printf("%d\n",ans);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值