BZOJ 3572: [Hnoi2014]世界树

题目大意:
给定一棵树,每次询问给定一些点,一个点会属于离他最近的给定点,问每个给定点有多少点属于他。

题解:

首先建立一棵虚树,求出虚树上每个点属于哪个点。

然后考虑一条边x->y,若x,y同属一个点,直接更新答案。

否则对于倍增出分界点,更新答案。

#include<cstdio>
#include<algorithm>
using namespace std;
int cnt,n,root,top,ti,sz[600005],dfn[600005],f[600005][20],last[600005],ed[600005],dep[600005],mark[600005],dis[600005],belong[600005],ans[600005],a[600005],q[600005],stack[600005];
struct node{
	int to,next,val;
}e[1000005];
void add(int a,int b,int c){
	e[++cnt].to=b;
	e[cnt].next=last[a];
	e[cnt].val=c;
	last[a]=cnt;
}
bool cmp(int x,int y){
	return dfn[x]<dfn[y];
}
int check(int x,int y){
	return dfn[x]<dfn[y] && dfn[y]<=ed[x];
}
void dfs(int x,int fa){ // 预处理 
	sz[x]=1;
	dfn[x]=++ti;
	f[x][0]=fa;
	for (int j=0; j<19; j++)
		f[x][j+1]=f[f[x][j]][j];
	for (int i=last[x]; i; i=e[i].next){
		int V=e[i].to;
		if (V==fa) continue;
		dep[V]=dep[x]+1;
		dfs(V,x);
		sz[x]+=sz[V];
	}
	ed[x]=ti;
}
void calc1(int x,int fa){ // 求子树内距离每个点最近的点是谁  -> belong[x] 距离 -> dis[x] 
//	printf("find:%d %d\n",x,fa);
	if (mark[x]){
		dis[x]=0;
		belong[x]=x;
	}
	else dis[x]=1e9;
	for (int i=last[x]; i; i=e[i].next){
		int V=e[i].to;
		if (V==fa) continue;
		calc1(V,x);
		if (dis[V]+e[i].val<dis[x] || dis[V]+e[i].val==dis[x] && belong[V]<belong[x]){
			belong[x]=belong[V];
			dis[x]=dis[V]+e[i].val;
		}
	}
}
void calc2(int x,int fa,int pre,int preval){ // 求每个点最近的点是谁  -> belong[x] 距离 -> dis[x]
	if (preval<dis[x] || preval==dis[x] && pre<belong[x]){
		belong[x]=pre;
		dis[x]=preval;
	}
	for (int i=last[x]; i; i=e[i].next){
		int V=e[i].to;
		if (V==fa) continue;
		calc2(V,x,belong[x],dis[x]+e[i].val);
	}
}
void pre(){
	calc1(root,0);
	calc2(root,0,0,1e9);
}
int lca(int x,int y){
	if (dep[x]<dep[y]) swap(x,y);
	for (int i=19; i>=0; i--){
		int to=f[x][i];
		if (dep[to]>=dep[y]) x=to;
	}
	if (x==y) return x;
	for (int i=19; i>=0; i--)
		if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	return f[x][0];
}
int query_Dis(int x,int y){ // x为 y 的祖先 
	return dep[y]-dep[x];
}
int query_dis(int x,int y){ // x->y 在原树上的距离 x不为 y的祖先 
	int LCA=lca(x,y);
	return query_Dis(LCA,x)+query_Dis(LCA,y);
}
int query(int x,int y){ // x->y 在原树上的最后一个点 
	for (int i=19; i>=0; i--){
		int to=f[x][i];
		if (dep[to]>dep[y]) x=to;
	}
	return x;
}
int query_fen(int x,int y){ // x->y 的分界点 z属于x 
	int Disx=dis[x],Disy=dis[y];
	int fx=x,fy=y;
	for (int i=19; i>=0; i--) {
		int to=f[x][i];
		if (dep[to]>dep[y]){
			if (Disx+query_Dis(to,fx)<Disy+query_Dis(fy,to) || 
				Disx+query_Dis(to,fx)==Disy+query_Dis(fy,to) && belong[fx]<belong[fy]){
					x=to;
				}
		}
	} 
	return x;
}
void solved(int x,int y,int z){ // x->y 统计边上的答案
	int fx=belong[x],fy=belong[y];
	if (belong[x]==belong[y]){ // 统计答案 	
		ans[belong[x]]+=sz[z]-sz[x];
	}
	else{ // 倍增找分界点 
		int z1=query_fen(x,y); // z1属于x 
		ans[belong[x]]+=sz[z1]-sz[x];
		ans[belong[y]]+=sz[z]-sz[z1];
	}
}
void solve(int x,int fa){ // 统计点上的答案 
	int ans1=sz[x];
	if (x==root) ans1=n;
	for (int i=last[x]; i; i=e[i].next){
		int V=e[i].to;
		if (V==fa) continue;
		int z=query(V,x); // V->x 在原树上的最后一个点
		ans1-=sz[z];
		solved(V,x,z);
		solve(V,fa);
	}
	ans[belong[x]]+=ans1;
}
int main(){
	scanf("%d",&n);
	for (int i=1; i<n; i++){
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y,0);
		add(y,x,0);
	}
	dep[1]=1;
	dfs(1,0);
	cnt=0;
	for (int i=1; i<=n; i++) last[i]=0;	
	int T;
	scanf("%d",&T);
	for (int i=1; i<=n; i++) dis[i]=1e9;
	while (T--){
		int n;
		scanf("%d",&n);
		for (int i=1; i<=n; i++){
			scanf("%d",&a[i]);
			q[i]=a[i];
		}
		sort(a+1,a+n+1,cmp);
		int len=n;
		for (int i=1; i<n; i++)
			a[++len]=lca(a[i],a[i+1]);
		sort(a+1,a+len+1,cmp);
		len=unique(a+1,a+len+1)-a-1;
		top=0;
		for (int i=1; i<=len; i++){
			while (top>0 && !check(stack[top],a[i])) top--;
			if (!stack[top]) root=a[i];
			else {
				int s=stack[top],t=a[i];
				add(s,t,query_dis(s,t)); 
			}
			stack[++top]=a[i];
		}
		for (int i=1; i<=n; i++) mark[q[i]]=1;
		pre();
		solve(root,0);
		for (int i=1; i<=n; i++) mark[q[i]]=0;
		for (int i=1; i<=n; i++)
			printf("%d ",ans[q[i]]);
		printf("\n");
		cnt=0;
		for (int i=1; i<=len; i++) last[a[i]]=0;
		for (int i=1; i<=n; i++) ans[q[i]]=0;
		for (int i=1; i<=len; i++) dis[a[i]]=1e9,belong[a[i]]=1e9;
	}
	return 0;
}

  

 

posted @ 2018-07-22 20:44  ~Silent  阅读(154)  评论(0编辑  收藏  举报
Live2D