费用流的应用
可以发现这道题与之前的费用流题有所不同,因为一个球队不论输还是赢都会获得收益。
这里就要用到一个技巧,我们一开始让比赛的双方都输,并且计算相应的收益,这样每场只要修改赢的那个人的收益就可以了。
然后就是每个人向终点连边,这里的费用随着流量的变化而变化,所以我们要用到拆边法。考虑一个人从赢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); }