zoukankan      html  css  js  c++  java
  • 【BZOJ2117】[2010国家集训队] Crash的旅游计划(动态点分治)

    点此看题面

    大致题意: 给定一棵无根树,求从每个点出发第(k)小的距离。

    前言

    感觉这才是真正的点分树模板题,比【BZOJ3730】震波要简单良心许多。

    本来已经做好了码上百行,肝一两个小时的准备,结果不知不觉就写完了。。。

    写完有几个小锅,也是简单调了调就揪出来了,总共用时也就半小时左右。

    大致思路

    一个很朴素的三只(log)的做法。

    对于每次询问,先二分答案(D),然后就变成了询问到该点距离小于等于(D)的点数。

    则考虑对于每个点我们开一个(vector)存下子树内所有点到它的距离,并对每个子节点开一个(vector)存下子树内所有点到它父节点的距离。

    我们在点分树上暴力往上跳,设与当前父亲距离为(d),则通过(upper\_bound)求出当前父亲子树内到其距离小于等于(D-d)的点数,然后以类似的方式求出上一个父亲子树内到当前父亲距离小于等于(D-d)的点数并从答案中减去(不然不仅会重复计算,而且还会出错)。

    应该可以说是比较套路的吧。

    二分一只(log),暴跳一只(log)(upper\_bound)一只(log),实际跑起来也不会很慢(毕竟这可是单点时限(15s)的题目啊)。

    代码

    #pragma GCC optimize(2)
    #pragma GCC optimize("inline")
    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define LN 20
    #define add(x,y,v) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].val=v)
    #define Gmax(x,y) (x<(y)&&(x=(y)))
    #define pb push_back
    using namespace std;
    int n,k,ee,lnk[N+5];struct edge {int to,nxt,val;}e[N<<1];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define pc(c) (C==E&&(clear(),0),*C++=c)
    		#define D isdigit(c=tc())
    		int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
    	public:
    		I FastIO() {A=B=FI,C=FO,E=FO+FS;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
    		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    		Tp I void writeln(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc('
    ');}
    		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
    		#undef D
    }F;
    namespace DynamicDotSolver//动态点分治
    {
    	int rt,Sz[N+5],Mx[N+5],used[N+5],t[N+5],f[N+5][LN+5],d[N+5][LN+5];
    	int T,Sx[N+5],Sd[N+5];vector<int> v[N+5],g[N+5];
    	I void GetRt(RI x,RI s,CI lst=0)//找重心
    	{
    		Sz[x]=1,Mx[x]=0;for(RI i=lnk[x];i;i=e[i].nxt) !used[e[i].to]&&
    			e[i].to^lst&&(GetRt(e[i].to,s,x),Sz[x]+=Sz[e[i].to],Gmax(Mx[x],Sz[e[i].to]));
    		Gmax(Mx[x],s-Sz[x]),Mx[x]<Mx[rt]&&(rt=x);
    	}
    	I void dfs(CI x,CI d=0,CI lst=0)//遍历子树
    	{
    		Sx[++T]=x,Sd[T]=d;for(RI i=lnk[x];i;i=e[i].nxt)//开栈存下每个点及其距离
    			!used[e[i].to]&&e[i].to^lst&&(dfs(e[i].to,d+e[i].val,x),0);
    	}
    	I void Build(RI x)//建点分树
    	{
    		RI i,y;for(used[x]=1,i=lnk[x];i;i=e[i].nxt) if(!used[e[i].to])
    		{
    			rt=0,GetRt(e[i].to,Sz[e[i].to]),dfs(e[i].to,e[i].val);
    			W(T) y=Sx[T],f[y][++t[y]]=x,v[x].pb(d[y][t[y]]=Sd[T]),g[rt].pb(Sd[T--]);Build(rt);//处理栈中信息
    		}
    	}
    	I void Init()//初始化
    	{
    		Mx[rt=0]=n,GetRt(1,n),Build(rt);for(RI i=1;i<=n;++i)
    			f[i][0]=i,reverse(f[i]+1,f[i]+t[i]+1),reverse(d[i]+1,d[i]+t[i]+1),//翻转父亲数组及其距离数组
    			sort(v[i].begin(),v[i].end()),sort(g[i].begin(),g[i].end());//给vector排序以便upper_bound
    	}
    	I bool Check(CI x,CI D)//验证二分的答案
    	{
    		#define GV(V,D) (upper_bound(V.begin(),V.end(),D)-V.begin())
    		RI p=GV(v[x],D);for(RI i=1;i<=t[x];++i) D>=d[x][i]&&//暴力上跳,当距离≤D时计算答案(注意不能break)
    			(p+=GV(v[f[x][i]],D-d[x][i])-GV(g[f[x][i-1]],D-d[x][i])+1);//减去上个父亲的贡献
    		return p>=k;
    	}
    }using namespace DynamicDotSolver;
    int main()
    {
    	RI i,x,y,z;for(F.read(n,k),i=1;i^n;++i) F.read(x,y,z),add(x,y,z),add(y,x,z);
    	RI l,r,mid;for(Init(),i=1;i<=n;++i)
    		{l=1,r=1e9;W(l<r) Check(i,mid=l+r-1>>1)?r=mid:l=mid+1;F.writeln(r);}//二分答案
    	return F.clear(),0;
    }
    
  • 相关阅读:
    VS2015编译OpenSSL1.0.2源码
    VS2015编译CURL7.54.0源码
    Mac OS Yosemite 文件批量重命名
    https 原理
    把本地仓库导入到Github
    jquery cdn加速注意事项
    关于CSS 里的_width是什么意思???
    HTML的footer置于页面最底部的方法
    html-include
    GitHub Permission to <<repository>> denied to <<username>>
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2117.html
Copyright © 2011-2022 走看看