bzoj 1449: [JSOI2009]球队收益
Description
![](http://www.lydsy.com/JudgeOnline/images/1449_1.jpg)
Input
![](http://www.lydsy.com/JudgeOnline/images/1449_2.jpg)
Output
一个整数表示联盟里所有球队收益之和的最小值。
Sample Input
3 3
1 0 2 1
1 1 10 1
0 1 3 3
1 2
2 3
3 1
1 0 2 1
1 1 10 1
0 1 3 3
1 2
2 3
3 1
Sample Output
43
HINT
solution
设n为当前winsum,m为当前losesum
这道题 有用的性质:
1.不管 输还是赢 增加的收益永远是递增的
Ci*(n+1)^2-Ci*n^2=2*n+1
2.对于单个的一支球队来说,赢得永远比输的收益多
技巧:
由于从比赛节点 向球队结点连一条边之后,还会有两种情况,根本无法加权值
所以先假设比赛的双方都输,然后改的时候只需要改赢得一方
赢得一方 增加的收益=Ci*(win+1)^2+Di(lose-1)^2-Ci*win^2-Di*lose^2
建图:S=0 T=n+m+1
1.S向每场比赛连 w=1 cost=0 的边
2.每场比赛向比赛双方连 w=1 cost=0 的边
3.记录下每个球队参加的比赛场数 s
每个球队 向T依次连 s条边,w=INF,cost按照一开始的winsum依次加 (这个比较难理解)
这样加保证 既选择了最小费用,又不会影响对手的最小费用 (从球队向T依次连了多条边)(大佬命名其为 拆边法)
最后跑MCMF即可
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<iostream> 5 #define mem(a,b) memset(a,b,sizeof(a)) 6 #define ll long long 7 #define dd double 8 using namespace std; 9 const int INF=(1<<31)-1; 10 inline int minn(int a,int b){return a<b?a:b;} 11 struct son 12 { 13 int u,v,next; 14 int w,cost; 15 }; 16 son a1[1000006]; 17 int first[1000006],e; 18 void addbian(int u,int v,int w,int cost) 19 { 20 a1[e].cost=cost; 21 a1[e].v=v; 22 a1[e].w=w; 23 a1[e].u=u; 24 a1[e].next=first[u]; 25 first[u]=e++; 26 } 27 void Link(int u,int v,int w,int cost) 28 { 29 addbian(u,v,w,cost); 30 addbian(v,u,0,-cost); 31 } 32 33 int n,m,u,o; 34 int win[5006],lose[5006],C[5006],D[5006]; 35 int a[5006],b[5006]; 36 int S,T; 37 int ans; 38 39 int flow[8006],flag[8006],d[8006],pre[8006]; 40 queue<int> q; 41 bool spfa(int &fw,int &ct) 42 { 43 mem(d,0x7f/3);mem(flag,0);mem(flow,0x7f);mem(pre,0); 44 int qqq=d[0]; 45 q.push(S);flag[S]=1;d[S]=0; 46 while(!q.empty()) 47 { 48 int now=q.front();q.pop();flag[now]=0; 49 for(int i=first[now];i!=-1;i=a1[i].next) 50 { 51 int temp=a1[i].v; 52 if(!a1[i].w||d[temp]<=d[now]+a1[i].cost)continue; 53 d[temp]=d[now]+a1[i].cost; 54 pre[temp]=i; 55 flow[temp]=minn(flow[now],a1[i].w); 56 if(!flag[temp]) 57 { 58 q.push(temp); 59 flag[temp]=1; 60 } 61 } 62 } 63 if(d[T]==qqq)return 0; 64 fw+=flow[T]; 65 ct+=d[T]*flow[T]; 66 int now=T; 67 while(now!=S) 68 { 69 a1[pre[now]].w-=flow[T]; 70 a1[pre[now]^1].w+=flow[T]; 71 now=a1[pre[now]].u; 72 } 73 return 1; 74 } 75 76 int MCMF() 77 { 78 int flow=0,cost=0; 79 while(spfa(flow,cost)); 80 return cost; 81 } 82 83 void out11() 84 { 85 printf("\n"); 86 for(int i=1;i<=m;++i) 87 { 88 printf("i=%d\n",i); 89 for(int j=first[i];j!=-1;j=a1[j].next) 90 printf("%d ",a1[j].v); 91 printf("\n"); 92 } 93 printf("\n"); 94 } 95 96 int main(){ 97 //freopen("1.txt","r",stdin); 98 mem(first,-1); 99 scanf("%d%d",&n,&m); 100 for(int i=1;i<=n;++i)scanf("%d%d%d%d",&win[i],&lose[i],&C[i],&D[i]); 101 S=0;T=n+m+1; 102 for(int i=1;i<=m;++i) 103 { 104 Link(S,i,1,0); 105 scanf("%d%d",&a[i],&b[i]); 106 ++lose[a[i]];++lose[b[i]]; 107 } 108 for(int i=1;i<=n;++i) 109 ans+=( C[i]*win[i]*win[i]+D[i]*lose[i]*lose[i] ); 110 for(int i=1;i<=m;++i) 111 { 112 Link(i,m+a[i],1,0); 113 Link(i,m+b[i],1,0); 114 Link(a[i]+m,T,1, C[a[i]]*(2*win[a[i]]+1)-D[a[i]]*(2*lose[a[i]]-1) ); 115 ++win[a[i]];--lose[a[i]]; 116 Link(b[i]+m,T,1, C[b[i]]*(2*win[b[i]]+1)-D[b[i]]*(2*lose[b[i]]-1) ); 117 ++win[b[i]];--lose[b[i]]; 118 } 119 //out11(); 120 //cout<<0; 121 printf("%d",ans+MCMF()); 122 //while(1); 123 return 0; 124 }