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
  • 相关阅读:
    53. Maximum Subarray
    64. Minimum Path Sum
    28. Implement strStr()
    26. Remove Duplicates from Sorted Array
    21. Merge Two Sorted Lists
    14. Longest Common Prefix
    7. Reverse Integer
    412. Fizz Buzz
    linux_修改域名(centos)
    linux_redis常用数据类型操作
  • 原文地址:https://www.cnblogs.com/alan-zhao-2007/p/p1099-sol.html
Copyright © 2011-2022 走看看