题解:
S连每场比赛流量1费用0
每场比赛连参赛队流量1费用0
我们发现调整一次 由win,lose变为 win+1,lose-1的费用为
(C*(win+1)^2+D*(lose-1)^2) - (C*win^2+D*lose^2)=C*(2*win+1)-D*(2*lose-1)
暴力连边就可以了
最后的答案=最想费用流+最初假设所有队伍都输的收益
代码:
#include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int N=100005; int S,W,lose[N],sum[N],win[N],ans,C[N],D[N]; int fi[N],n,t,cas,m,x,y,z,f[N],ne[N],num,zz[N],fl[N],gp[N],dist[N],pre[N],sl[N]; void jb(int x,int y,int z,int s) { ne[num]=fi[x]; fi[x]=num; zz[num]=y; sl[num]=z; fl[num++]=s; ne[num]=fi[y]; fi[y]=num; zz[num]=x; sl[num]=0; fl[num++]=-s; } int spfa() { memset(dist,0x3f,sizeof dist); memset(pre,-1,sizeof pre); memset(gp,0,sizeof gp); memset(f,0,sizeof f); queue<int > Q; Q.push(S); dist[S]=0; while (!Q.empty()) { int now=Q.front(); Q.pop(); f[now]=0; for (int i=fi[now];i!=-1;i=ne[i]) if (sl[i]>0) { int t=zz[i]; if (dist[t]>dist[now]+fl[i]) { dist[t]=dist[now]+fl[i]; pre[t]=now; gp[t]=i; if (!f[t]) { f[t]=1; Q.push(t); } } } } if (pre[W]==-1)return 1; return 0; } void Max_flow() { int cost=0,flow=0; while (!spfa()) { int f=1e9; for (int i=W;i!=S;i=pre[i]) f=min(f,sl[gp[i]]); cost+=f; flow+=dist[W]*f; for (int i=W;i!=S;i=pre[i]) { sl[gp[i]]-=f; sl[gp[i]^1]+=f; } } printf("%d\n",flow+ans); } int main() { memset(fi,-1,sizeof fi); scanf("%d%d",&n,&m); S=n+m+1,W=S+1; for (int i=1;i<=n;i++)scanf("%d%d%d%d",&win[i],&lose[i],&C[i],&D[i]); for (int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); jb(S,n+i,1,0); jb(n+i,x,1,0); jb(n+i,y,1,0); sum[x]++,sum[y]++; } for (int i=1;i<=n;i++)lose[i]+=sum[i]; for (int i=1;i<=n;i++)ans+=C[i]*win[i]*win[i]+D[i]*lose[i]*lose[i]; for (int i=1;i<=n;i++) for (int j=1;j<=sum[i];j++) { jb(i,W,1,C[i]*(2*win[i]+1)-D[i]*(2*lose[i]-1)); win[i]++;lose[i]--; } Max_flow(); }