题目大意:给定n支球队。第i支球队已经赢了wini场。输了losei场,接下来还有m场比赛。每一个球队终于的收益为Ci∗x2i+Di∗y2i,当中xi为终于的胜场,yi为终于的负场
求最小化收益考虑一仅仅球队,其收益与在接下来的比赛中的胜场数关系为:
赢0场 Ci∗win2i+Di∗(di+losei)2 赢1场 Ci∗(wini+1)2+Di∗(di+losei−1)2 赢2场 Ci∗(wini+2)2+Di∗(di+losei−2)2 … 赢di场 Ci∗(wini+di)2+Di∗lose2i差分后可得:
赢第1场 Ci∗(2∗wini+1)−Di∗[2∗(di+losei)−1] 赢第2场 Ci∗(2∗wini+3)−Di∗[2∗(di+losei)−3] … 赢第di场 Ci∗[2∗wini+(2∗di−1)]−Di∗[2∗(di+losei)−(2∗di−1)]easy发现差分后单调递增。故收益是关于胜场数的一个下凸函数,能够拆边做
于是我们将每支球队和每场比赛都变成一个点,建图跑费用流
源点向第i个点连di条边。流量为1。第j条边的费用为Ci∗[2∗wini+(2∗j−1)]−Di∗[2∗(di+losei)−(2∗j−1)]
每场比赛的两方向这场比赛连一条流量为1费用为0的边 每场比赛向汇点连一条流量为1费用为0的边最小费用+∑ni=1[Ci∗win2i+Di∗(di+losei)2]就是答案
#include#include #include #include #define M 6060#define S 0#define T (M-1)#define INF 0x3f3f3f3fusing namespace std;int n,m,ans;int win[M],lose[M],C[M],D[M],d[M];namespace Min_Cost_Max_Flow{ struct abcd{ int to,flow,cost,next; }table[1001001]; int head[M],tot=1; void Add(int x,int y,int f,int c) { table[++tot].to=y; table[tot].flow=f; table[tot].cost=c; table[tot].next=head[x]; head[x]=tot; } void Link(int x,int y,int f,int c) { Add(x,y,f,c); Add(y,x,0,-c); } bool Edmonds_Karp() { static int q[65540],cost[M],flow[M],from[M]; static unsigned short r,h; static bool v[M]; int i; memset(cost,0x3f,sizeof cost); cost[S]=0;flow[S]=INF;q[++r]=S; while(r!=h) { int x=q[++h];v[x]=false; for(i=head[x];i;i=table[i].next) if(table[i].flow&&cost[table[i].to]>cost[x]+table[i].cost) { cost[table[i].to]=cost[x]+table[i].cost; flow[table[i].to]=min(flow[x],table[i].flow); from[table[i].to]=i; if(!v[table[i].to]) v[table[i].to]=true,q[++r]=table[i].to; } } if(cost[T]==0x3f3f3f3f) return false; ans+=cost[T]*flow[T]; for(i=from[T];i;i=from[table[i^1].to]) table[i].flow-=flow[T],table[i^1].flow+=flow[T]; return true; }}int main(){ using namespace Min_Cost_Max_Flow; int i,j,x,y; cin>>n>>m; for(i=1;i<=n;i++) scanf("%d%d%d%d",&win[i],&lose[i],&C[i],&D[i]); for(i=1;i<=m;i++) { scanf("%d%d",&x,&y); Link(x,n+i,1,0); Link(y,n+i,1,0); Link(n+i,T,1,0); d[x]++;d[y]++; } for(i=1;i<=n;i++) { ans+=C[i]*win[i]*win[i]+D[i]*(d[i]+lose[i])*(d[i]+lose[i]); for(j=1;j<=d[i];j++) Link(S,i,1, C[i]*(2*win[i]+j*2-1)-D[i]*(2*(d[i]+lose[i])-j*2+1) ); } while( Edmonds_Karp() ); cout< <