[JSOI2009]球队收益

题目

这题好神啊

我们发现一个球队的总比赛场数是确定的,设第(i)支球队一共进行了(s_i)场比赛

于是这个球队的收益就是(c_i imes x^2+d_i(s_i-x)^2)

我们拆开柿子可以发现(c_ix^2+d_ix^2+d_is_i^2-2xs_id_i)

我们拿出和(x)有关的项来发现((c_i+d_i)x^2-2s_id_ix)

现在我们把贡献变成只和胜场数(x)有关了,考虑(x+1)的时候这个柿子的增量,我们发现是((2x+1)(c_i+d_i)-2s_id_i),我们发现这个增量是单增的

于是我们现在可以这样建图

对于每一场比赛我们建一个点,源点向这个点连一条流量为(1)费用为(0)的边

这个点向对应的两支球队也连一条流量为(1)费用为(0)的边

对应的两支球队向汇点连流量为(1)费用为对应增量的边,同时我们让这两支球队的胜场数的加(1)

由于增量是单增的,我们跑的是一个最小费用流,所以肯定会优先选择那些费用较小的也就是对应的胜场数较少的边了

最后的答案就是费用流跑出来的答案加上初始的贡献

代码

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
std::queue<int> q;
const int inf=999999999;
const int maxn=6005;
struct E{int v,nxt,w,f;}e[maxn*10];
int n,m,num=1,S,T;
int head[maxn],vis[maxn],dis[maxn];
int a[maxn],b[maxn],s[maxn],c[maxn],d[maxn],x[maxn],y[maxn];
inline void C(int x,int y,int f,int w) {e[++num].v=y;e[num].nxt=head[x];head[x]=num;e[num].w=w;e[num].f=f;}
inline void add(int x,int y,int f,int w) {C(x,y,f,w),C(y,x,0,-1*w);}
inline int SPFA() {
	for(re int i=S;i<=T;i++) dis[i]=inf,vis[i]=0;
	dis[T]=0,q.push(T);
	while(!q.empty()) {
		int k=q.front();q.pop();vis[k]=0;
		for(re int i=head[k];i;i=e[i].nxt)
		if(e[i^1].f&&dis[e[i].v]>dis[k]+e[i^1].w) {
			dis[e[i].v]=dis[k]+e[i^1].w;
			if(!vis[e[i].v]) vis[e[i].v]=1,q.push(e[i].v);
		}
	}
	return dis[S]<inf;
}
int dfs(int x,int now) {
	if(x==T||!now) return now;
	int flow=0,ff;vis[x]=1;
	for(re int i=head[x];i;i=e[i].nxt)
	if(!vis[e[i].v]&&e[i].f&&dis[e[i].v]==dis[x]+e[i^1].w) {
		ff=dfs(e[i].v,min(e[i].f,now));
		if(ff<=0) continue;
		now-=ff,flow+=ff,e[i].f-=ff,e[i^1].f+=ff;
		if(!now) break;
	}
	return flow;
}
inline int zkw() {
	int ans=0;
	while(SPFA()) {
		vis[T]=1;
		while(vis[T]) {
			for(re int i=S;i<=T;i++) vis[i]=0;
			ans+=dfs(S,inf)*dis[S];
		}
	}
	return ans;
}
int main() {
	n=read(),m=read();
	for(re int i=1;i<=n;i++) a[i]=read(),b[i]=read(),c[i]=read(),d[i]=read();
	for(re int i=1;i<=m;i++) x[i]=read(),y[i]=read(),s[x[i]]++,s[y[i]]++;
	for(re int i=1;i<=n;i++) s[i]+=a[i]+b[i];
	int ans=0;T=n+m+1;
	for(re int i=1;i<=n;i++) ans+=c[i]*a[i]*a[i]+d[i]*(s[i]-a[i])*(s[i]-a[i]);
	for(re int i=1;i<=m;i++) {
		add(S,n+i,1,0),add(n+i,x[i],1,0),add(n+i,y[i],1,0);
		add(x[i],T,1,(2*a[x[i]]+1)*(c[x[i]]+d[x[i]])-2*d[x[i]]*s[x[i]]);
		add(y[i],T,1,(2*a[y[i]]+1)*(c[y[i]]+d[y[i]])-2*d[y[i]]*s[y[i]]);
		a[x[i]]++,a[y[i]]++;
	}
	printf("%d
",ans+zkw());
	return 0;
}

原文地址:https://www.cnblogs.com/asuldb/p/10767047.html