bzoj 1449: [JSOI2009]球队收益

Description

Input

Output

一个整数表示联盟里所有球队收益之和的最小值。

Sample Input

3 3
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即可

  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 }
mk A_LEAF

 

posted @ 2017-07-31 16:19  A_LEAF  阅读(109)  评论(0编辑  收藏  举报