bzoj1758Wc10重建计划——solution
Posted Der Barde, Nietzsche
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj1758Wc10重建计划——solution相关的知识,希望对你有一定的参考价值。
1758: [Wc2010]重建计划
Time Limit: 40 Sec Memory Limit: 162 MBSubmit: 4707 Solved: 1200
[Submit][Status][Discuss]
Description
Input
第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案,每行三个正整数Ai,Bi,Vi分别表示道路(Ai,Bi),其价值为Vi 其中城市由1..N进行标号
Output
输出最大平均估值,保留三位小数
Sample Input
2 3
1 2 1
1 3 2
1 4 3
Sample Output
HINT
N<=100000,1<=L<=U<=N-1,Vi<=1000000 新加数据一组 By leoly,但未重测..2016.9.27
省选R1虽有些不足之处(D2T1写炸了,还被卡了一个点的常数),不过基于我那点NOIP分,能苟到这个排名就不错了,知足吧~~,(SD一直到前50之前都没有NOIP比我低的......)
如上图所示
这时我们发现,每个点a只会在枚举到他所在的重链的顶端的父亲时对效率造成log的影响
因为:
在枚举a所在的重链中的其他祖先节点时,a所在的子树都是作为深度最深的那个而没有被枚举(如枚举b的子树时,a所在的子树是最深的,没有被枚举深度)
在枚举更靠上的重链内部时,a所在的子树也是作为深度最深的那个而没有被枚举(如枚举e的子树时,a在d这个子树内,作为最深的存在,没有被枚举深度)
在枚举更靠上的重链顶端的父亲时,虽然a所在的子树需要枚举,但由于我们枚举的是深度,所以因为这个子树有更深的链所以这个效率应该算作那个更深的链的效率(如当枚举d的子树时,尽管a所在的子树被枚举了深度,但这个效率应该被算在cf链上)
所以这个方法可以做到nlogn
这个“按子树最大深度剖分树链,对长链链接的子节点不做操作”的技巧被称作一种长链剖分
于是我们完美地用$Nlog_2^2N$解决了这个问题
(upd 2018.5.24:不采用线段树合并,转而采用基于长链剖分的暴力线段树插入,好像也可以保证效率)
代码:
#include<cstdio> #include<cstring> #include<algorithm> #define LL long long using namespace std; const LL exp=10000; const LL INF=1e15; struct ss{ int to,next; LL val; }e[200010]; int first[100010],num; struct DT{ LL max; int ch[2]; }data[3000010]; int root[100010],tot; int dep[100010],depst[100010],hw[100010]; LL val[100010]; LL l,r,mid,ans; int n,Low,Top; void build(int ,int ,LL ); bool check(LL ); void dfs_1(int ,int ); void dfs_2(int ); void insert(int ,int ,int&,int ,LL ); LL get_max(int ,int ,int ,int ,int ); LL get_poi_max(int ,int ,int ,int ); void merge(int ,int ,int ,int&); int main() { int i,j,k,o; scanf("%d",&n); scanf("%d%d",&Low,&Top); for(i=1;i<n;i++){ scanf("%d%d%d",&j,&k,&o); build(j,k,o*exp),build(k,j,o*exp); } dfs_1(1,0); l=exp,r=1e10,mid=(l+r)>>1ll; while(l<r-3){ if(check(mid)) l=mid; else r=mid-1; mid=(l+r)>>1ll; } for(mid=r;mid>=l;mid--) if(check(mid)){ printf("%.3lf",mid/10000.0); return 0; } } void build(int f,int t,LL v){ e[++num].next=first[f]; e[num].to=t,e[num].val=v; first[f]=num; } bool check(LL lim){ int i; memset(root,0,sizeof(root)),tot=0; memset(data,0,sizeof(data)),data[0].max=-3*INF; for(i=1;i<=num;i++) e[i].val-=lim; ans=-3e10; dfs_2(1); for(i=1;i<=num;i++) e[i].val+=lim; return ans>=0; } void dfs_1(int now,int fa){ int i; dep[now]=dep[fa]+1; depst[now]=dep[now],hw[now]=-1; for(i=first[now];i;i=e[i].next) if(e[i].to!=fa){ dfs_1(e[i].to,now); if(depst[now]<depst[e[i].to]) depst[now]=depst[e[i].to],hw[now]=i; } } void dfs_2(int now){ int i,j; if(hw[now]!=-1){ val[e[hw[now]].to]=val[now]+e[hw[now]].val; dfs_2(e[hw[now]].to); root[now]=root[e[hw[now]].to]; if(dep[now]+Low<=depst[now]); ans=max(ans,get_max(1,n,root[now],dep[now]+Low,min(depst[now],dep[now]+Top))-val[now]); insert(1,n,root[now],dep[now],val[now]); } else{ insert(1,n,root[now],dep[now],val[now]); return ; } for(i=first[now];i;i=e[i].next) if(dep[e[i].to]>dep[now]&&i!=hw[now]){ val[e[i].to]=val[now]+e[i].val; dfs_2(e[i].to); for(j=dep[e[i].to];j<=depst[e[i].to]&&j<=dep[now]+Top;j++) ans=max(ans,get_poi_max(1,n,root[e[i].to],j)-val[now]+get_max(1,n,root[now],max(dep[now]*2+Low-j,dep[now]+1),min(dep[now]*2+Top-j,depst[now]))-val[now]); merge(1,n,root[e[i].to],root[now]); } } void insert(int l,int r,int&now,int lim,LL x){ if(!now)now=++tot; if(l==r){ data[now].max=x; return ; } int mid=(l+r)>>1; if(lim<=mid) insert(l,mid,data[now].ch[0],lim,x); else insert(mid+1,r,data[now].ch[1],lim,x); data[now].max=max(data[data[now].ch[0]].max,data[data[now].ch[1]].max); } LL get_max(int l,int r,int now,int L,int R){ if(L>R)return data[0].max; if(L<=l&&r<=R) return data[now].max; int mid=(l+r)>>1; LL lm=-INF,rm=-INF; if(L<=mid) lm=get_max(l,mid,data[now].ch[0],L,R); if(R>mid) rm=get_max(mid+1,r,data[now].ch[1],L,R); if(lm>rm) return lm; return rm; } LL get_poi_max(int l,int r,int now,int lim){ if(l==r)return data[now].max; int mid=(l+r)>>1; if(lim<=mid) return get_poi_max(l,mid,data[now].ch[0],lim); else return get_poi_max(mid+1,r,data[now].ch[1],lim); } void merge(int l,int r,int pre,int&now){ if(!now||!pre){ now+=pre; return ; } if(l==r){ data[now].max=max(data[now].max,data[pre].max); return ; } int mid=(l+r)>>1; merge(l,mid,data[pre].ch[0],data[now].ch[0]); merge(mid+1,r,data[pre].ch[1],data[now].ch[1]); data[now].max=max(data[data[now].ch[0]].max,data[data[now].ch[1]].max); }
(bzoj卡到39S......)
以上是关于bzoj1758Wc10重建计划——solution的主要内容,如果未能解决你的问题,请参考以下文章
bzoj 1758 [Wc2010]重建计划 分数规划+树分治单调队列check