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 }
贴个zkw的代码
AC without art, no better than WA !