【3】题解:P3233 [HNOI2014] 世界树

非常有质量的神仙虚树题。

点击图片打开题目。

![]( https://cdn.luogu.com.cn/upload/image_hosting/jzm64xq9.png?x-oss-process=image/resize ,m_lfit,h_170,w_225)

这个链接被直接解析崩坏了,建议去原题那里看。

题意简述:给你 \(q\) 次查询,每一个点去最近的一个议会点,求之后每一个议会点来了多少个点。

考虑分类讨论:

  1. 虚树上点的归属:
    • 分给自己。
    • 分给父亲。
    • 分给儿子。
  2. 其他点的归属:
    • 只有一个固定归属点的。
    • 在两个虚树点之间被抢来抢去的。

靠多个 dfs 解决。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e5+10,INF=1e9;
vector<int>e[N],g[N];
int fat[N][25];
int dfn[N],dfncnt;
int sz[N],dep[N]; 
int T,k[N],kk[N];
int sta[2*N],top;
int ans[N];
struct dp_now{
	int f,mn;
}f[N];
vector<int>vir;
map<int,int>issp;
void dfs0(int p){ // 清空 dp 
	f[p].mn=INF,ans[p]=0;
	for(auto v:g[p])
		dfs0(v);
} 
void dfs(int p,int fa){
	dfn[p]=++dfncnt;
	sz[p]=1;
	for(int i=1;i<=20;i++)
		fat[p][i]=fat[fat[p][i-1]][i-1];
	for(auto v:e[p])if(v!=fa){
		dep[v]=dep[p]+1;
		fat[v][0]=p;
		dfs(v,p);
		sz[p]+=sz[v];
	}
}
int deps(int u,int v){
	if(dep[u]<dep[v])	
		swap(u,v);
	return dep[u]-dep[v];
}
void dfs2(int p){ // 子树的贡献 
	if(issp[p])
		f[p].f=p,f[p].mn=0;
	for(auto v:g[p]){
		dfs2(v);
		if(
			(f[v].mn+deps(v,p)<f[p].mn)||
			(f[v].mn+deps(v,p)==f[p].mn&&(f[p].f>f[v].f)) 
		)
			f[p].f=f[v].f,
			f[p].mn=f[v].mn+deps(v,p);
	}
	return;
}
void dfs3(int p,int fa){ // 父亲的贡献 
	if(fa){
		if(
			(f[fa].mn+deps(fa,p)<f[p].mn)||
			(f[fa].mn+deps(fa,p)==f[p].mn&&(f[p].f>f[fa].f)) 
		)
			f[p].f=f[fa].f,
			f[p].mn=f[fa].mn+deps(fa,p);			
	}
	ans[f[p].f]++;
	for(auto v:g[p])
		dfs3(v,p); 
	return;
}
pair<int,int> vs(int u,int v){ // 争抢客户 
	bool rev=0;
	if(dep[u]<dep[v])
		swap(u,v),rev=1;
	int umn=f[u].mn,newu=u;
	for(int i=20;i>=0;i--)
		if(umn<f[v].mn+dep[u]-dep[v]-(2<<i))
			f[fat[u][i]].f=f[u].f,
			u=fat[u][i],umn+=(1<<i);
	if(umn==f[v].mn+dep[u]-dep[v]-2&&f[u].f<f[v].f)
		u=fat[u][0];	
	int haveu=sz[u]-sz[newu];
	for(int i=20;i>=0;i--)if(dep[newu]-(1<<i)>dep[v])
		newu=fat[newu][i];
	int havev=sz[newu]-sz[u];
	if(rev)	
		swap(haveu,havev);
	return {haveu,havev};
}
void dfs4(int p){ // 虚树外的点 	 
	int son=sz[p]-1;
	for(auto v:g[p]){
		auto Give=vs(p,v);
		ans[f[p].f]+=Give.first;
		ans[f[v].f]+=Give.second;
		son-=(sz[v]+Give.first+Give.second);
		dfs4(v);
	} 
	ans[f[p].f]+=son;
}
void dfs5(int p){ // 清空虚树 
	for(auto v:g[p])
		dfs5(v);
	g[p].clear();
}
int LCA(int u,int v){
	if(dep[u]<dep[v])
		swap(u,v);
	for(int i=20;i>=0;i--)if(dep[u]-(1<<i)>=dep[v])
		u=fat[u][i];
	if(u==v)return u;
	for(int i=20;i>=0;i--)if(fat[u][i]!=fat[v][i])
		u=fat[u][i],v=fat[v][i];
	return fat[u][0];
}
bool cmp(int u,int v){
	return dfn[u]<dfn[v];
}
void build(){
	cin>>T;
	issp.clear();
	vir.clear();
	for(int i=1;i<=T;i++)
		cin>>k[i],kk[i]=k[i],issp[k[i]]=1;
	sort(k+1,k+T+1,cmp);
	top=0;
	sta[++top]=1;
	for(int i=1;i<=T;i++)if(k[i]!=1){
		int lca=LCA(k[i],sta[top]);
		if(lca!=sta[top]){
			while(dfn[sta[top-1]]>=dfn[lca])
				g[sta[top-1]].push_back(sta[top]),
				--top;
			if(sta[top]!=lca)
				g[lca].push_back(sta[top]),
				sta[top]=lca; 
		}else;
		sta[++top]=k[i];
	}
	while(top>1)
		g[sta[top-1]].push_back(sta[top]),
		--top;	
	return;
}
signed main(){
	int n;
	cin>>n;
	for(int i=1;i<n;i++){
		int u,v;
		cin>>u>>v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	fat[1][0]=1;
	dfs(1,0);
	int m;
	cin>>m;
	while(m--){
		build();
		dfs0(1);
		dfs2(1);
		dfs3(1,0);
		dfs4(1); 
		for(int i=1;i<=T;i++)
			cout<<ans[kk[i]]<<" ";
		cout<<"\n";
		dfs5(1);
	}
	return 0;
}

https://www.luogu.com.cn/article/66jgmi6p

posted @ 2026-10-12 11:32  Ivan422  阅读(4)  评论(0)    收藏  举报