Codeforces 516D - Drazil and Morning Exercise(树的直径+并查集)

Codeforces 题目传送门 & 洛谷题目传送门

这是一道 jxd 的作业题,感觉难度不是特别大(虽然我并没有自己独立 AC,不过也可能是省选结束了我的脑子也没了罢(((,就随便写写罢

u1s1 这题似乎是 dreamoon 出的呢(

首先我们需求出 \(d_x\)\(\max\limits_{y=1}^n\text{dist}(x,y)\),这个想怎么求怎么求,可以使用换根 \(dp\),也可以用树的直径,复杂度 \(\mathcal O(n)\)

求完之后该怎么做呢?注意到 \(q\) 很小,因此有一个很显然的想法是将所有点按 \(d\) 从小到大排序然后暴力双针+LCT,不过一来我不会 LCT,二来这样询问复杂度会达到 \(n\log n\),虽然应该可以通过,但并不优秀,因此我们暂时不讲这个做法。我们不妨来探究 \(d\) 的性质,记 \(r\) 为使 \(d_u\) 取到最小值的 \(u\),显然 \(u\) 在树的直径上,否则我们可以将其移到直径上并使得 \(d\) 值不会变得更大。

我们不妨以 \(r\) 为根将原先的无根树变成一棵有根树,那么有一个性质:对于任何 \(v\)\(u\) 的祖先均有 \(d_v\ge d_u\)。证明:首先假设一条树的直径为 \(x,y\),我们先证明一个引理:对于任意节点 \(t\ne u\),记 \(ind_t\) 为使 \(\text{dist}(t,v)\) 取到最大值的 \(v\),那么 \(ind_t\) 一定不在 \(t\) 的子树中,考虑反证法,假设 \(ind_t\)\(t\) 的子树中,那么一定有 \(d_r\ge \text{dist}(r,ind_t))>\text{dist}(t,ind_t)=d_t\),不符合 \(d_r\) 最小的条件,矛盾。显然原命题等价于对于所有 \(u\) 及其父亲 \(f\) 都有 \(d_f\le d_u\),考虑某个点 \(u\) 及其父亲 \(f\),若 \(f\ne r\),由于 \(ind_u\) 不在 \(u\) 的子树中,必然有 \(\text{dist}(ind_u,u)=\text{dist}(ind_u,f)+\text{dist}(u,f)\),又由于 \(ind_f\) 不在 \(f\) 的子树中,必然也有 \(ind_f\) 不在 \(u\) 的子树中,因此 \(\text{dist}(ind_u,f)=d_f\),又由于 \(\text(dist)(u,f)>0\),故 \(d_f<d_u\),否则由 \(d_r\) 为最小的 \(d_i\) 可知命题显然成立。

有了这个性质,我们可以用 two-pointers 的方法,枚举连通块中最小的 \(d_u\),并 two pointers 维护所有 \(d_u\le d_v\le d_u+l\)\(v\),根据上面的推论在这样的 \(v\) 组成的子图中,\(u\) 所在的连通块必定是以 \(u\) 为根的一个虚树,并查集即可,虽然这里需要支持删点,看上去不太可做,不过显然删除的点并不会影响连通性,用并查集维护联通块大小是正确的。

时间复杂度 \(n\log n+qn\alpha(n)\)

const int MAXN=1e5;
int n,hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],val[MAXN*2+5],ec=0;
void adde(int u,int v,int w){to[++ec]=v;val[ec]=w;nxt[ec]=hd[u];hd[u]=ec;}
ll dis[MAXN+5],d[MAXN+5];
void dfs(int x,int f){
	chkmax(d[x],dis[x]);
	for(int e=hd[x];e;e=nxt[e]){
		int y=to[e],z=val[e];if(y==f) continue;
		dis[y]=dis[x]+z;dfs(y,x);
	}
} int ord[MAXN+5],fa[MAXN+5];
void getfa(int x,int f){
	fa[x]=f;
	for(int e=hd[x];e;e=nxt[e]){
		int y=to[e];if(y==f) continue;
		getfa(y,x);
	}
}
bool cmp(int x,int y){return d[x]<d[y];}
int f[MAXN+5],siz[MAXN+5];
int find(int x){return (!f[x])?x:f[x]=find(f[x]);}
void merge(int x,int y){x=find(x);y=find(y);if(x^y) f[x]=y,siz[y]+=siz[x];}
int main(){
	scanf("%d",&n);
	for(int i=1,u,v,w;i<n;i++){
		scanf("%d%d%d",&u,&v,&w);
		adde(u,v,w);adde(v,u,w);
	} dfs(1,0);int rt=0;
	for(int i=1;i<=n;i++) if(dis[i]>dis[rt]) rt=i;
	dis[rt]=0;dfs(rt,0);rt=0;
	for(int i=1;i<=n;i++) if(dis[i]>dis[rt]) rt=i;
	dis[rt]=0;dfs(rt,0);for(int i=1;i<=n;i++) ord[i]=i;
	sort(ord+1,ord+n+1,cmp);getfa(ord[1],0);
//	for(int i=1;i<=n;i++) printf("%lld%c",d[i],(i==n)?'\n':' ');
//	for(int i=1;i<=n;i++) printf("%d%c",ord[i],(i==n)?'\n':' ');
//	for(int i=1;i<=n;i++) printf("%d%c",fa[i],(i==n)?'\n':' ');
	int qu;scanf("%d",&qu);
	while(qu--){
		ll l;int ans=0;scanf("%lld",&l);
		for(int i=1;i<=n;i++) f[i]=0,siz[i]=1;
		for(int i=n,j=n;i;i--){
			while(d[ord[j]]>d[ord[i]]+l) siz[find(ord[j])]--,j--;
//			printf("%d %d %d\n",i,j,siz[find(ord[i])]);
			chkmax(ans,siz[find(ord[i])]);if(fa[ord[i]]) merge(ord[i],fa[ord[i]]);
		} printf("%d\n",ans);
	}
	return 0;
}
posted @ 2021-04-14 23:19  tzc_wk  阅读(55)  评论(0)    收藏  举报