Living-Dream 系列笔记 第63期

Posted on 2024-07-24 17:00  _XOFqwq  阅读(4)  评论(0编辑  收藏  举报

树的中心

当选取树上节点 \(u\) 为根时,最长链最短,则称 \(u\) 为树的中心。

  • 性质一:至多 \(2\) 个且一定相邻。

  • 性质二:一定位于树的直径上。

  • 性质三:若一棵树有多条直径,则它们必定交于树的中心。

  • 性质四:树的中心为根时,根到直径两端点分别为最长 / 次长链。

U392706

板子。

code
#include<bits/stdc++.h>
using namespace std;

const int N=1e6+5;
int n;
struct E{
	int v,w;
};
vector<E> G[N];
vector<int> ans;
int len1[N],len2[N],up[N];

void dfsd(int cur,int fa){
	for(auto i:G[cur]){
		if(i.v==fa) continue;
		dfsd(i.v,cur);
		if(len1[i.v]+i.w>len1[cur])
			len2[cur]=len1[cur],
			len1[cur]=len1[i.v]+i.w;
		else if(len1[i.v]+i.w>len2[cur])
			len2[cur]=len1[i.v]+i.w;
	}
}
void dfsu(int cur,int fa){
	for(auto i:G[cur]){
		if(i.v==fa) continue;
		up[i.v]=up[cur]+i.w;
		if(len1[i.v]+i.w!=len1[cur])
			up[i.v]=max(up[i.v],len1[cur]+i.w);
		else
			up[i.v]=max(up[i.v],len2[cur]+i.w);
		dfsu(i.v,cur);
	}
}

int main(){
	cin>>n;
	for(int i=1,u,v,w;i<n;i++)
		cin>>u>>v>>w,
		G[u].push_back({v,w}),
		G[v].push_back({u,w});
	dfsd(1,0),dfsu(1,0);
	int mini=1e9;
	for(int i=1;i<=n;i++){
		if(mini>max(len1[i],up[i]))
			mini=max(len1[i],up[i]),ans.clear(),ans.push_back(i);
		else if(mini==max(len1[i],up[i]))
			ans.push_back(i);
	}
	sort(ans.begin(),ans.end());
	for(int i:ans) cout<<i<<'\n';
	return 0;
} 

CF1294F

显然三个点选两个树的直径上的点 \(u,v\) 更优。

至于第三个点 \(w\),我们可以认为 \(w\) 一定会与 \(u \to v\) 的路径交于 \(lca(w,v)\)

于是枚举 \(w\) 求 LCA 并取最大即可,\(O(n \log n)\)

当然更简单而高效的方法就是以 \(u \to v\) 上的点进行多起点 BFS 求最长路即可,\(O(n)\)

这里提供的实现是法二。

code
#include<bits/stdc++.h>
using namespace std;

const int N=2e5+5;
int n;
int x,y,dis,pre[N];
int z,ans;
vector<int> G[N<<1];
bool vis[N];
struct Edge{ int u,sum; };

void dfs(int cur,int sum){
	if(sum>=dis) dis=sum,y=cur;
	for(int i:G[cur])
		if(i!=pre[cur])
			pre[i]=cur,dfs(i,sum+1);
}
void bfs(){
	queue<Edge> q;
	int now=y; //z=pre[y];
	while(now) 
		q.push({now,0}),vis[now]=1,now=pre[now];
	while(!q.empty()){
		Edge cur=q.front(); q.pop();
		if(cur.sum>=ans&&cur.u!=x&&cur.u!=y) ans=cur.sum,z=cur.u;
		for(int i:G[cur.u])
			if(!vis[i])
				q.push({i,cur.sum+1}),vis[i]=1;
	}
	
}

int main(){
	cin>>n;
	for(int i=1,u,v;i<n;i++)
		cin>>u>>v,
		G[u].push_back(v),
		G[v].push_back(u);
	pre[1]=0,dfs(1,0);
    x=y,pre[x]=0,dfs(x,0);
	bfs();
	cout<<dis+ans<<'\n'<<x<<' '<<y<<' '<<z;
	return 0;
} 

AT_agc001_c

反向思考,我们要求最少删除的点数,即是求最多保留的点数。

于是我们对于 \(k\) 进行奇偶性分讨,从而构造直径为 \(k\) 的树,在所有这些里面取保留点数最多的即可。

code
#include<bits/stdc++.h>
using namespace std;

const int N=2e3+5;
int n,k,tot,ans;
vector<int> G[N<<1];

void dfs(int cur,int fa,int sum){
	tot++;
	if(sum==k/2) return;
	for(int i:G[cur])
		if(i!=fa)
			dfs(i,cur,sum+1);
}

int main(){
	cin>>n>>k;
	for(int i=1,u,v;i<n;i++)
		cin>>u>>v,
		G[u].push_back(v),
		G[v].push_back(u);
	if(k%2==0)
		for(int i=1;i<=n;i++)
			tot=0,dfs(i,0,0),ans=max(ans,tot);
	else
		for(int i=1;i<=n;i++)
			for(int j:G[i])
				tot=0,dfs(i,j,0),dfs(j,i,0),ans=max(ans,tot);
	cout<<n-ans;
	return 0;
}