【BZOJ1758】【Wc2010】重建计划 分数规划+树分治单调队列check
2015-03-18 18:12
330 查看
广告:
#include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/44418207"); }
题解:
首先比较显然会想到分数规划模型。不太好想,先放过。
我们树分治处理经过每个点的路径。
然后对于在[L,R][L,R]区间内的每个长度记录最长距离。
然后每棵子树跟之前记录的数组处理一下,算出当前这棵子树中点为一端点,经过根节点(重心)的最优答案,然后再更新记录最长距离的数组。
貌似很科学,但是这个更新答案的过程,实际上是O(n2)O(n^2)的,并不能接受。
所以就有了下述神奇的处理方法:
我们进行分数规划,二分答案,这样新的边权赋值为len-ans(mid),然后check答案是否>0。。
而check的过程中对于每个当前子树中的最优深度函数值,我们只需要取之前记录的最优的一项更新就好了。我们可以把当前子树中的函数值按深度从小到大来更新答案,这个过程中使用单调队列维护一个深度上升权值(新赋值的权值)上升的序列,然后每次取队尾更新答案即可。
这个过程是线性的,而对于全部子树 ii 深度最长距离的数组的更新也只是取个max,它也是线性的。
这样就可以在O(nlog2n)O(nlog^2n)的时间内出解了。
二分写在里面还是写在外面?
然后有一个细节问题,就是二分写在在对每个重心处理时还是写在树分治的外面的问题。网上很多都说写在里面快,而我的同学 PoPoQQQ大爷PoPoQQQ大爷 实测是写在外面快。
所以当然是写在外面快!!!
(并不是因为“实测”,而是因为这是大爷说的!!! 2333)
下面给出一定的分析。
首先写在里面的话是对于每个重心更新答案之后,之后的重心在处理时下边界会增大,这样的话实际上时间受影响于每次在当前重心更新的幅度。
而写在外面的话,则是严格完整时间复杂度?并不是的。我们在某个重心进行处理时发现符合要求的答案时,可以直接跳出。
写在里面是一点一点调整答案,但是调整的幅度欠佳,因为还有一个上界未被改变(这个我不确定是否有下调上界的剪枝,没有细想,不要D我)。
而写在外面则是大步跳,每次得到mid以后期望很快得到一个重心可以求出符合答案的解,也就是很多重心不需要被处理,剪枝的幅度比较大。
代码:
这个代码是刚开始不知道单调队列分数规划shenmegui,然后一点点扒的别人代码,发现看不懂都是因为人家代码太渣太丑了,其实思想水得要命,而且实现上也非常简单。。总之这个代码也是一款又渣又丑的产品,如果要扒代码请一定要跳过这个代码。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 101000 #define INF 1e13 #define eps 1e-4 #define V e[i].v using namespace std; struct Eli { int v,len,next; }e[N<<1]; int head ,cnt; inline void add(int u,int v,int len) { e[++cnt].v=v; e[cnt].len=len; e[cnt].next=head[u]; head[u]=cnt; } int can ,size ,deep ,q ; int L,R,tl,tr; int n,length,dep; int root,DEP; double a ,b ,sum ; double ans,mid; inline void getroot(int x,int p) { int d=0; size[x]=1; for(int i=head[x];i;i=e[i].next) if(can[V]&&V!=p) { getroot(V,x); size[x]+=size[V]; d=max(d,size[V]); } d=max(n-size[x],d); if(d<length)length=d,root=x; } inline void dfs(int x,int p) { size[x]=1; deep[x]=deep[p]+1; for(int i=head[x];i;i=e[i].next){ if(can[V]&&V!=p){ dfs(V,x); size[x]+=size[V]; } } if(deep[x]>DEP)DEP=deep[x]; } inline void dfs1(int x,int p) { if(deep[x]>R)return; // 此深度不能经过重心 a[deep[x]]=max(a[deep[x]],sum[x]); for(int i=head[x];i;i=e[i].next) { if(can[V]&&V!=p) { sum[V]=sum[x]+e[i].len-mid; dfs1(V,x); } } if(deep[x]>dep)dep=deep[x]; } bool check() { double mx,tmp,maxx; int pos,i,j; b[0]=0; for(i=1;i<=DEP;i++)b[i]=-INF; tmp=-INF;maxx=-INF; int maxp=0; for(i=head[root];i;i=e[i].next)if(can[V]) { tl=tr=0; a[0]=0; for(j=1;j<=DEP;j++)a[j]=-INF; // 枚举深度 a[i]记录此子树深度为i的最大权 dep=0; sum[V]=e[i].len-mid; // 目前只要处理出深度i时的最长,所以mid不用*路径长度 dfs1(V,root); // 处理a数组,同时记录当前子树中最深深度。 if(maxp)q[++tr]=maxp; for(j=1;j<=dep;j++) { if(j>R)break; if(tl<tr&&q[tl+1]>R-j)tl++; if(L>=j&&L-j<=DEP) { while(tl<tr&&b[q]<b[L-j])tr--; q[++tr]=L-j; tmp=max(tmp,b[q[tl+1]]+a[j]); } else if(j>L)tmp=max(tmp,b[q[tl+1]]+a[j]); } for(j=1;j<=dep;j++) // b_i是当前整棵树的深度i最大权 { b[j]=max(b[j],a[j]); if(j>=L&&j<=R&&maxx<b[j]) maxx=b[j],maxp=j; } } return tmp>0; } void work(int x) { length=n,getroot(x,0); // 找树的重心 if(n<=L)return; DEP=0; dfs(root,0); // 处理每个点以重心为根的deep和深度,同时记录最大深度 double l=ans,r=1e10; while(l<r-eps) // 分数规划 { mid=(l+r)/2; if(check())l=mid; else r=mid; } ans=l; can[root]=0; // 递归分治 for(int i=head[root];i;i=e[i].next) if(can[V])n=size[V],work(V); } int main() { scanf("%d%d%d",&n,&L,&R); int x,y,z; for(int i=1;i<n;i++) { scanf("%d%d%d",&x,&y,&z); add(x,y,z),add(y,x,z); } memset(can,-1,sizeof can); deep[0]=-1; work(1); printf("%.3lf\n",ans); return 0; } 相关文章推荐
- BZOJ1758: [Wc2010]重建计划(01分数规划+点分治+单调队列)
- 【BZOJ 1758】【WC 2010】重建计划 分数规划+点分治+单调队列
- bzoj 1758 [Wc2010]重建计划 01分数规划 点分治 单调队列
- 【BZOJ1758】【WC2010】重建计划(点分治,单调队列)
- bzoj 1758: [Wc2010]重建计划 (01分数规划+点分治)
- 【bzoj1758】[Wc2010]重建计划 二分答案+单调队列+点分治
- BZOJ1758 [Wc2010]重建计划(二分答案+点分治+单调队列)
- bzoj1758 [Wc2010]重建计划(点分治+01分数规划)
- BZOJ 1758 Wc2010 重建计划 树的点分治+二分+单调队列
- 【BZOJ】【P1758】【Wc2010】【重建计划】【题解】【点分治+二分+单调队列】
- [BZOJ1758] [WC2010] 重建计划 - 树分治
- Bzoj:1758:[Wc2010]重建计划:树的点分治
- 【BZOJ1758】重建计划,点分治+单调队列
- wc2010 bzoj1758(点分治+二分+单调队列) TLE
- 【BZOJ1758】【Wc2010】重建计划 树的点分治 二分
- bzoj1758 [Wc2010]重建计划(二分答案+点分治+单调栈)
- 树的点分治 bzoj1758【WC2010】重建计划
- bzoj 1758: [Wc2010]重建计划 长链剖分+线段树
- bzoj1758 [Wc2010]重建计划
- Bzoj1758: [Wc2010]重建计划