bzoj1449 [JSOI2009]球队收益

  这是一道凸费用的题,需要用到拆边的转化。

  具体是这样,首先列出关于流量xi的费用式子:f(xi)=ci*xi2+di*(si-xi)2=(ci+di)*xi2-2*di*si*xi+di*si 其中si表示i进行的总常数,xi表示胜场数(也就是规定的流量)。显然这样的式子是不能定费用的,但我们可以求一下每增加1的流量费用的改变量:f(xi)-f(xi-1)=(ci+di)*(2*xi-1)-2*di*si 这样我们可以把每1单位的流量设置一个费用,也就是把原来一条边拆成若干条边,第i条边如果对应第wi个胜场,那么它的费用应该是(ci+di)*(2*wi-1)-2*di*si,注意这里wi是第i个胜场,是要算上已经确定的胜场数的。

  这样建好图,跑spfa或者zkw就行了,但是zkw要注意一点,就是有负费用边!

  zkw里面dis顶标的意义是汇点出发到各点的最短路(以容量大于0的边的费用为边权),那么有负费用的话顶标就不能初始化为0了,而应该是一个负值。于是我们需要spfa来初始化一下顶标。这样做麻烦了不少,不过还是有意义的:我这个题一下子跑到了rank1......

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cmath>
  5 #include<cstring>
  6 #include<ctime>
  7 #define maxn 6200
  8 #define maxm 52000
  9 #define inf 2147483647
 10 using namespace std;
 11 struct et
 12 {
 13     int s,t,next,val,cost;
 14 }e[maxm];
 15 int dis[maxn],fir[maxn],q[maxn],v[maxn],sum[maxn],win[maxn],lose[maxn],d[maxn],c[maxn];
 16 bool inque[maxn];
 17 int n,m,tot,st,ed;
 18 long long ans;
 19 
 20 void prepare()
 21 {
 22     for (int i=st;i<=ed;i++) dis[i]=inf;
 23     int head=0,tail=1;
 24     q[1]=ed; dis[ed]=0; inque[ed]=1;
 25     while (head<tail)
 26     {
 27         int now=q[++head];
 28         for (int j=fir[now];j;j=e[j].next)
 29         {
 30             int k=e[j].t;
 31             if (e[j^1].val&&dis[k]>dis[now]+e[j^1].cost)
 32             {
 33                 dis[k]=dis[now]+e[j^1].cost;
 34                 if (!inque[k]) q[++tail]=k,inque[k]=1;
 35             }
 36         }
 37         inque[now]=0;
 38     }
 39 }
 40 
 41 int dfs(int now,int flow)
 42 {
 43     if (now==ed)
 44     {
 45         ans=ans+(long long)dis[st]*flow;
 46         return flow;
 47     }
 48     v[now]=1;
 49     int sap=0;
 50     for (int j=fir[now];j;j=e[j].next)
 51     {
 52         int k=e[j].t;
 53         if (!v[k]&&e[j].val&&dis[now]==dis[k]+e[j].cost)
 54         {
 55             int tmp=dfs(k,min(e[j].val,flow-sap));
 56             e[j].val-=tmp;
 57             e[j^1].val+=tmp;
 58             sap+=tmp;
 59             if (sap==flow) return sap;
 60         }
 61     }
 62     return sap;
 63 }
 64 
 65 bool adjust()
 66 {
 67     int tmp=inf;
 68     for (int i=st;i<=ed;i++) 
 69         if (v[i]) for (int j=fir[i];j;j=e[j].next)
 70         {
 71             int k=e[j].t;
 72             if (!v[k]&&e[j].val) tmp=min(tmp,dis[k]+e[j].cost-dis[i]);
 73         }
 74     if (tmp==inf) return 0;
 75     for (int i=st;i<=ed;i++)
 76         if (v[i]) dis[i]+=tmp;
 77     return 1;
 78 }
 79 
 80 void add(int x,int y,int z,int w)
 81 {
 82     e[++tot].s=x; e[tot].t=y; e[tot].val=z; e[tot].cost=w; e[tot].next=fir[x]; fir[x]=tot;
 83     e[++tot].s=y; e[tot].t=x; e[tot].val=0; e[tot].cost=-w; e[tot].next=fir[y]; fir[y]=tot;
 84 }
 85 
 86 int main()
 87 {
 88     scanf("%d%d",&n,&m);
 89     st=0; ed=n+m+1; tot=1;
 90     for (int i=1;i<=n;i++)
 91     {
 92         scanf("%d%d%d%d",&win[i],&lose[i],&c[i],&d[i]);
 93         sum[i]=win[i]+lose[i];
 94     }
 95     int x,y;
 96     for (int i=1;i<=m;i++)
 97     {
 98         scanf("%d%d",&x,&y);
 99         add(st,i,1,0);
100         add(i,m+x,1,0);
101         add(i,m+y,1,0);
102         sum[x]++; sum[y]++;
103     }
104     for (int i=1;i<=n;i++)
105         for (int j=win[i]+1;j<=sum[i]-lose[i];j++)
106             add(m+i,ed,1,(c[i]+d[i])*(2*j-1)-2*d[i]*sum[i]);
107     //for (int i=2;i<=tot;i++) cout<<e[i].s<<' '<<e[i].t<<' '<<e[i].val<<' '<<e[i].cost<<endl;
108     //计算初始收益
109     for (int i=1;i<=n;i++)
110         ans+=c[i]*win[i]*win[i]+d[i]*(sum[i]-win[i])*(sum[i]-win[i]);
111     prepare();
112     do
113         do memset(v,0,sizeof(v));
114         while (dfs(st,inf));
115     while (adjust());
116     printf("%lld\n",ans);
117     return 0;
118 }
income

  贴个zkw的代码

转载于:https://www.cnblogs.com/zig-zag/archive/2013/05/15/3079931.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值