Codefoces 516D. Drazil and Morning Exercise题解

原题链接:516D. Drazil and Morning Exercise

题目大意:给定一棵\(n\)个点的树,定义\(f_x=\max_{i=1}^n \text{dist}(x,i)\)\(q\)次询问,每次给出一个\(l\),求树上一个最大的连通块\(S\),使得\(\max_{x \in S}-\min_{x \in S}\leq l\),求最大的连通块大小。


题解:首先,看到\(q \leq 50\),很容易想到这是一个时间复杂度在\(O(nq)\)左右的算法,然后注意到对于每一个点,离它最远的点一定是直径的两端点之一,所以\(f_x\)可以在\(O(n)\)的时间内求出。接下来考虑对于每一个询问怎么处理。先给出一个算法,然后再解释这个算法为什么是正确的:将所有点按照\(f_x\)从大到小排序,然后用 two-pointers 从大到小扫,令 two-pointers 的两个端点为\(i,j(f_i \leq f_j)\),第\(i\)个点把与它相邻的且之前访问过的点并进去(或者说是与它相邻的\(f\)值更大的点),对于\(f_j-l>f_i\)\(j\)这个点直接删掉。

好了,为什么这么做是正确的呢?我们考虑这棵树如果是以任意一条直径的中点为根,那么对于每一个节点,它的孩子节点的\(f\)值一定比它大(证明的话,随便画一画应该就可以了),那么每次当前节点被删掉时,以它为根的子树一定被删空了,所以所得的点是一定联通的。

接下来是代码:

#include <cstdio>
#include <algorithm>
using namespace std;
template<typename Elem>
void read(Elem &a){
	a=0;
	char c=getchar();
	while(c<'0'||c>'9'){
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		a=(a<<1)+(a<<3)+(c^48);
		c=getchar();
	}
}
const int Maxn=100000;
typedef long long ll;
int head[Maxn+5],arrive[Maxn<<1|5],val[Maxn<<1|5],nxt[Maxn<<1|5],tot;
void add_edge(int from,int to,int value){
	arrive[++tot]=to;
	val[tot]=value;
	nxt[tot]=head[from];
	head[from]=tot;
}
int n,q;
ll f[Maxn+5];
ll dis[Maxn+5];
int root;
int id[Maxn+5];
int fa[Maxn+5],sz[Maxn+5];
bool vis[Maxn+5];
void init_dfs(int u,int fa){
	f[u]=max(f[u],dis[u]);
	for(int i=head[u];i;i=nxt[i]){
		int v=arrive[i];
		if(v==fa){
			continue;
		}
		dis[v]=dis[u]+val[i];
		init_dfs(v,u);
	}
}
bool cmp(int p,int q){
	return f[p]>f[q];
}
int find(int x){
	if(fa[x]==x){
		return x;
	}
	return fa[x]=find(fa[x]);
}
void merge(int x,int y){
	int fa_x=find(x),fa_y=find(y);
	if(fa_x==fa_y){
		return;
	}
	fa[fa_y]=fa_x;
	sz[fa_x]+=sz[fa_y];
}
int main(){
	read(n);
	int u,v,w;
	for(int i=1;i<n;i++){
		read(u),read(v),read(w);
		add_edge(u,v,w);
		add_edge(v,u,w);
	}
	dis[1]=0;
	root=1;
	init_dfs(root,0);
	for(int i=1;i<=n;i++){
		if(dis[i]>dis[root]){
			root=i;
		}
	}
	dis[root]=0;
	init_dfs(root,0);
	for(int i=1;i<=n;i++){
		if(dis[i]>dis[root]){
			root=i;
		}
	}
	dis[root]=0;
	init_dfs(root,0);
	for(int i=1;i<=n;i++){
		if(f[i]<f[root]){
			root=i;
		}
	}
	for(int i=1;i<=n;i++){
		id[i]=i;
	}
	sort(id+1,id+1+n,cmp);
	read(q);
	int ans;
	ll l;
	for(int i=1;i<=q;i++){
		ans=1;
		read(l);
		for(int j=1;j<=n;j++){
			fa[j]=j;
			sz[j]=1;
			vis[j]=0;
		}
		for(int j=1,k=1;j<=n;j++){
			while(k<j&&f[id[k]]-f[id[j]]>l){
				sz[find(id[k])]--;
				k++;
			}
			vis[id[j]]=1;
			for(int to=head[id[j]];to;to=nxt[to]){
				int v=arrive[to];
				if(!vis[v]){
					continue;
				}
				merge(id[j],v);
			}
			ans=max(ans,sz[find(id[j])]);
		}
		printf("%d\n",ans);
	}
	return 0;
}
posted @ 2020-02-29 10:00  with_hope  阅读(139)  评论(0编辑  收藏  举报