[World Final 2016] Branch Assignment
链接
先求出正置边和反置边时b+1到前b个点的最短路dis[0/1][x](x∈[1,b]),
令D[x]=dis[0][x]+dis[1][x]
然后分组后每个x对代价的贡献为D[x]*(所在组中元素个数-1)
考虑DP决策分组过程,发现没有一个很好的序,
不过为了使得代价小,应该把D小的放在个数大的组里,D大的放在个数少的组里
由此可以想出,应该是大小相近的元素放在了同一组中,这意味着,先排序,在划分成s段,的所有情况包含了原问题的最优解
于是把D[1~b]排序后,有方程
$f[i][j]=MIN_{k=0}^{j-1}[f[i-1][k]+(j-k-1)*(sum[j]-sum[k])]$
$O(n^3)$
其中:f[i][j]表示把前j个分成i组的最小值,sum为D的前缀和
非法状态均置为INF
由于可以粗略的知道这个东西在j确定,i为自变量的函数图像上有凸性
(可以玄学地认为,最开始的时候,多划分一刀可以造成很大的改变,随着划分次数越来越多,多划分一刀的改变越来越微小)
于是,可以使用带权二分来优化这一DP
即,消去对划分次数的限制,通过二分来找到一个合适的划分附加代价,使得即使不限制划分次数,最后的最优解也恰好满足我们对划分数的限制
这样有了一个新的方程
$f'[j]=MIN_{k=0}^{j-1}[f'[k]+(j-k-1)*(sum[j]-sum[k])]+C$
$O(n^2log)$
考虑优化转移过程
拆开方程得到
$f'[j]=MIN_{k=0}^{j-1}[f'[k]+(k+1)sum[k]-ksum[j]-jsum[k]]+(j-1)sum[j]+C$
在方程中
当固定j不动时
随k增加,f'[k]+(k+1)sum[k]项增加,-ksum[j]-jsum[k]项减小
而随j的增加,含j的项(即-ksum[j]-jsum[k])对答案的影响加剧,于是,随j的增加,j的最优决策中的k会单调变大(随j的增加,我们决策时更为看重含j项,为了使含j项减小,我们试图使用更大的k)
这意味着这个方程有决策单调性
本题通过带权二分和决策单调性优化可以做到$O(nlog^2)$
(感觉最近状态很差,午休一直睡不着来着,几个月了吧,先是用N^2log给方哥号上贡献了半屏T,又以为可以nlog做,然后贡献了半屏Wa,最后才发现决策单调性)
代码:
1 #include<queue> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define LL long long 6 #define P pair <int ,int > 7 using namespace std; 8 priority_queue <P ,vector <P > ,greater <P > > PQ ; 9 int n,b,s,r; 10 struct INP{ 11 int u,v,val; 12 }inp[50010]; 13 struct ss{ 14 int to,next,val; 15 }e[50010]; 16 int first[5010],num; 17 LL d[5010],f[5010],sum[5010]; 18 int lin[5010],dis[5010],que[5010],grt_st[5010]; 19 void build(int ,int ,int ); 20 void dij(int ); 21 bool check(LL ); 22 LL ask(int ,int ); 23 int main() 24 { 25 int i,j,k,l; 26 LL L,R,mid; 27 scanf("%d%d%d%d",&n,&b,&s,&r); 28 for(i=1;i<=r;i++){ 29 scanf("%d%d%d",&inp[i].u,&inp[i].v,&inp[i].val); 30 build(inp[i].u,inp[i].v,inp[i].val); 31 } 32 dij(b+1); 33 for(i=1;i<=b;i++) d[i]=dis[i]; 34 memset(first,0,sizeof(first)),num=0; 35 for(i=1;i<=r;i++) 36 build(inp[i].v,inp[i].u,inp[i].val); 37 dij(b+1); 38 for(i=1;i<=b;i++) d[i]+=dis[i]; 39 sort(d+1,d+b+1); 40 for(i=1;i<=b;i++) sum[i]=sum[i-1]+d[i]; 41 L=0,R=b*sum[b],mid=(L+R)>>1; 42 while(R-L>=3){ 43 if(check(mid)) L=mid; 44 else R=mid-1; 45 mid=(L+R)>>1; 46 } 47 for(mid=R;mid>=L;mid--) 48 if(check(mid)){ 49 printf("%lld\n",f[b]-s*mid); 50 return 0; 51 } 52 return 0; 53 } 54 void build(int f,int t,int val){ 55 e[++num].next=first[f]; 56 e[num].to=t,e[num].val=val; 57 first[f]=num; 58 } 59 void dij(int S){ 60 int i,U; 61 memset(dis,0x3f,sizeof(dis)); 62 dis[S]=0; 63 P u,v; 64 u.first=0,u.second=S; 65 PQ.push(u); 66 while(!PQ.empty()){ 67 u=PQ.top(); 68 PQ.pop(); 69 U=u.second; 70 for(i=first[U];i;i=e[i].next) 71 if(dis[e[i].to]>dis[U]+e[i].val){ 72 dis[e[i].to]=dis[U]+e[i].val; 73 v.first=dis[e[i].to],v.second=e[i].to; 74 PQ.push(v); 75 } 76 } 77 } 78 bool check(LL lim){ 79 int tmp=0,i,h=0,t=1; 80 int l,r,mid; 81 f[0]=lin[0]=0,grt_st[0]=1; 82 que[t]=0; 83 for(i=1;i<=b;i++){ 84 l=h+1,r=t,mid=(l+r)>>1; 85 while(r-l>3){ 86 if(i>=grt_st[que[mid]]) l=mid; 87 else r=mid-1; 88 mid=(l+r)>>1; 89 } 90 for(mid=r;mid>=l;mid--) 91 if(i>=grt_st[que[mid]]){ 92 f[i]=ask(i,que[mid])+lim,lin[i]=lin[que[mid]]+1; 93 break; 94 } 95 if(i==b) break; 96 grt_st[i]=b+1; 97 while(h<t&&grt_st[que[t]]>i&&ask(grt_st[que[t]],i)<=ask(grt_st[que[t]],que[t])) grt_st[i]=grt_st[que[t]],t--; 98 if(h<t){ 99 l=max(grt_st[que[t]],i+1),r=grt_st[i]-1,mid=(l+r)>>1; 100 while(r-l>3){ 101 if(ask(mid,i)<=ask(mid,que[t])) r=mid; 102 else l=mid+1; 103 mid=(l+r)>>1; 104 } 105 for(mid=l;mid<=r;mid++) 106 if(ask(mid,i)<=ask(mid,que[t])){ 107 grt_st[i]=mid; 108 break; 109 } 110 } 111 if(grt_st[i]!=b+1) 112 que[++t]=i; 113 } 114 return lin[b]>=s; 115 } 116 LL ask(int x,int typ){ 117 return f[typ]+(x-typ-1)*(sum[x]-sum[typ]); 118 }