zoukankan      html  css  js  c++  java
  • P1099 [NOIP2007 提高组] 树网的核

    题解

    首先可以发现,如果原树有多条直径,那么在任意一条直径上求得的答案都是一样的。

    于是任选一条直径 (sleftrightarrow t),令原树以 (s) 为根,在这条直径上枚举答案。

    这时候实际上可以用 dfs 序+线段树做到 (O(n log n)),但不够优。

    我们知道,树的直径有这样一个性质:任选在直径上的一个点 (u),那么满足 (operatorname{LCA}(u,v)in sleftrightarrow operatorname{fa}(u)) 的所有点 (v)(operatorname{dist}(u,v)) 一定小于等于 (operatorname{dist}(u,s))

    对所有在直径上的 (u),预处理 (d_u) 表示从 (u) 开始,不经过直径上的其他点,得到的最长路径。于是,当枚举的路径是 (uleftrightarrow v) 时,答案就是 (max{maxlimits_{xin uleftrightarrow v} {d_x} ,operatorname{dist}(s,u),operatorname{dist}(v,t)}),可以用单调队列维护。

    但,根据直径的性质,(maxlimits_{xin uleftrightarrow v} {d_x}=maxlimits_{xin sleftrightarrow t}{d_x}),可以预先算出来。

    这样就可以做到 (O(n)) 了。

    代码
    #include <cstdio>
    #include <cstring>
    #include <cctype>
    #include <vector>
    #include <algorithm>
    using namespace std;
    #define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
    #define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
    template<typename T> void Read(T &x){
    	x=0;int _f=1;
    	char ch=getchar();
    	while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();
    	while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
    	x=x*_f;
    }
    template<typename T,typename... Args> void Read(T &x,Args& ...others){
    	Read(x);Read(others...);
    }
    typedef long long ll;
    const int N=5e5+5;
    int n,s;vector<pair<int,ll>> G[N];
    ll dep[N];
    int Diameter(int u,int fa){
    	int res=u;
    	for(auto i:G[u]){
    		int v=i.first,x;
    		if(v==fa) continue;
    		dep[v]=dep[u]+i.second;
    		if(dep[x=Diameter(v,u)]>dep[res]) res=x;
    	}
    	return res;
    }
    int fa[N];
    void Dfs(int u){
    	for(auto i:G[u]){
    		if(i.first==fa[u]) continue;
    		fa[i.first]=u,dep[i.first]=dep[u]+i.second;
    		Dfs(i.first);
    	}
    }
    int vis[N];
    vector<int> pth;ll mxdep[N];
    void Dfs1(int u,ll d){
    	mxdep[u]=d;
    	for(auto i:G[u]){
    		int v=i.first;
    		if(v==fa[u]||vis[v]) continue;
    		Dfs1(v,d+i.second);mxdep[u]=max(mxdep[u],mxdep[v]);
    	}
    }
    int main(){
    	Read(n,s);
    	For(i,1,n-1){
    		int u,v,w;Read(u,v,w);
    		G[u].push_back({v,w}),G[v].push_back({u,w});
    	}
    	int st=Diameter(1,0),ed=Diameter(st,0);
    //	printf("%d %d
    ",st,ed);
    	dep[st]=0;Dfs(st);
    //	For(i,1,n) printf("%lld ",dep[i]);puts("");
    	for(int u=ed;u!=st;u=fa[u]){
    		pth.push_back(u),vis[u]=1;
    	}
    	pth.push_back(st),vis[st]=1;
    	reverse(pth.begin(),pth.end());
    	for(int u:pth) Dfs1(u,0);
    //	For(i,1,n) printf("%lld ",mxdep[i]);puts("");
    	ll mx=0;
    	for(int u:pth) mx=max(mx,mxdep[u]);
    	ll ans=0x3f3f3f3f3f3f3f3fLL,d1=0,d2=dep[ed];
    	for(int i=0,j=0;i<pth.size()&&j<pth.size();++i){
    		d1=dep[pth[i]];
    		while(j<pth.size()&&dep[pth[j]]-dep[pth[i]]<=s) d2=dep[ed]-dep[pth[j++]];
    		--j;
    //		printf("%d %d %lld %lld
    ",i,j,d1,d2);
    		ans=min(ans,max({mx,d1,d2}));
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    
    Written by Alan_Zhao
  • 相关阅读:
    Linux makefile教程之概述一[转]
    Valid Parentheses
    Letter Combinations of a Phone Number
    机器学习经典分类算法 —— C4.5算法(附python实现代码)
    3Sum Closest
    3Sum
    Integer to Roman
    寒假文献阅读(四)
    Longest Common Prefix
    Roman to Integer
  • 原文地址:https://www.cnblogs.com/alan-zhao-2007/p/p1099-sol.html
Copyright © 2011-2022 走看看