「CF516D」 Drazil and Morning Exercise

「CF516D」 Drazil and Morning Exercise

传送门

这个 \(f_i\) 显然可以通过树形 \(\texttt{DP}\) 直接求。

然后看到这种差值问题感觉就可以二分转换为判定性问题。

哦不好像本来就是判定性问题

显然我们可以考虑枚举最小值,然后检查其他点的合法情况,然后最后查最小值所在连通块大小即可。

这样做是 \(O(n^2\alpha(n))\) 的。

考虑优化。我们猜想这个 \(f\) 一定有性质。

注意到当最小值单调不降的时候,最大值一定也单调不降,也即是说,我们需要确定一个顺序使得我们可以用一个类似于双指针的过程来寻找答案。

有一个非常显然的结论:\(f\) 值最小的节点一定是直径的中点或中点左右的实际节点。

那么有这个东西之后我们可以推得:若以这个点为根,那么一定有 \(f_u<f_{fa_u}\),证明非常简单。

实际上实现的过程中,我们可以对最大值进行枚举,因为最大值的缺失一定不会影响当前连通块的连通性,相当于是以这个 \(f\) 值最小的点为根的某颗子树的叶节点,直接将子树大小减一即可。

然后这个题就完了。

/*---Author:HenryHuang---*/
/*---Never Settle---*/
/*---Never Enough---*/
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
typedef long long ll;
struct edge{
	int to,nex,w;
}e[maxn<<1];
int head[maxn],cnt;
void add(int a,int b,int c){
	e[++cnt]=(edge){b,head[a],c};
	head[a]=cnt;
}
ll f[maxn],g1[maxn],g2[maxn],cho[maxn];
int ff[maxn];
void dfs1(int u,int fa){
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].to;
		if(v==fa) continue;
		dfs1(v,u);
		if(e[i].w+g1[v]>g1[u]){
			g2[u]=g1[u];
			g1[u]=e[i].w+g1[v];
			cho[u]=v;
		}
		else g2[u]=max(g2[u],e[i].w+g1[v]);
	}
}
void dfs2(int u,int fa){
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].to;
		if(v==fa) continue;
		if(cho[u]==v) f[v]=max(f[u],g2[u])+e[i].w;
		else f[v]=max(f[u],g1[u])+e[i].w;
		dfs2(v,u);
	}
}
void dfs3(int u,int fa){
	ff[u]=fa;
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].to;
		if(v==fa) continue;
		dfs3(v,u);
	}
}
int fa[maxn],siz[maxn],id[maxn];
int getfa(int t){
	if(fa[t]==t) return fa[t];
	return fa[t]=getfa(fa[t]);
}
void merge(int x,int y){
	x=getfa(x),y=getfa(y);
	fa[y]=x,siz[x]+=siz[y];
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	int n;cin>>n;
	for(int i=1;i<n;++i){
		int a,b,c;cin>>a>>b>>c;
		add(a,b,c),add(b,a,c);
	}
	dfs1(1,0);
	dfs2(1,0);
	for(int i=1;i<=n;++i) f[i]=max(f[i],g1[i]);
	int mn=1;
	for(int i=1;i<=n;++i)
		if(f[i]<f[mn]) mn=i;
	for(int i=1;i<=n;++i) id[i]=i;
	sort(id+1,id+n+1,[&](int x,int y){return f[x]<f[y];});
	dfs3(mn,0);
	int q;cin>>q;
	while(q--){
		ll num;cin>>num;
		for(int i=1;i<=n;++i) fa[i]=i,siz[i]=1;
		int l=n,ans=0;
		for(int r=n;r>=1;--r){
			while(f[id[r]]-f[id[l]]<=num&&l>1){
				--l;
				if(f[id[r]]-f[id[l]]<=num){
					for(int i=head[id[l]];i;i=e[i].nex){
						int v=e[i].to;
						if(v==ff[id[l]]) continue;
						merge(id[l],v);
					}
					ans=max(ans,siz[getfa(id[l])]);
				}
				else{
					++l;
					break;
				}
			}
			--siz[getfa(id[r])];
		}
		cout<<ans<<'\n';
	}
	return 0;
}
上一篇:hdu 1175(广搜)


下一篇:[2021 Spring] CS61B 课后练习 HW 0: A Java Crash Course