1. 程式人生 > 其它 >P1099 [NOIP2007 提高組] 樹網的核 - 樹的直徑

P1099 [NOIP2007 提高組] 樹網的核 - 樹的直徑

題解

首先可以發現,如果原樹有多條直徑,那麼在任意一條直徑上求得的答案都是一樣的。

於是任選一條直徑 \(s\leftrightarrow t\),令原樹以 \(s\) 為根,在這條直徑上列舉答案。

這時候實際上可以用 dfs 序+線段樹做到 \(O(n \log n)\),但不夠優。

我們知道,樹的直徑有這樣一個性質:任選在直徑上的一個點 \(u\),那麼滿足 \(\operatorname{LCA}(u,v)\in s\leftrightarrow \operatorname{fa}(u)\) 的所有點 \(v\)\(\operatorname{dist}(u,v)\) 一定小於等於 \(\operatorname{dist}(u,s)\)

對所有在直徑上的 \(u\),預處理 \(d_u\) 表示從 \(u\) 開始,不經過直徑上的其他點,得到的最長路徑。於是,當列舉的路徑是 \(u\leftrightarrow v\) 時,答案就是 \(\max\{\max\limits_{x\in u\leftrightarrow v} \{d_x\} ,\operatorname{dist}(s,u),\operatorname{dist}(v,t)\}\),可以用單調佇列維護。

但,根據直徑的性質,\(\max\limits_{x\in u\leftrightarrow v} \{d_x\}=\max\limits_{x\in s\leftrightarrow 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\n",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\n",i,j,d1,d2);
		ans=min(ans,max({mx,d1,d2}));
	}
	printf("%lld\n",ans);
	return 0;
}
Written by Alan_Zhao