zoukankan      html  css  js  c++  java
  • 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
    ",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("
    ");
    		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;
    }
    

      

  • 相关阅读:
    #leetcode刷题之路32-最长有效括号
    #leetcode刷题之路31-下一个排列
    数据结构第一节 递归
    数据结构 第一节
    java基础 抽象类与接口
    Spring1
    java基础3。定义类
    java基础2。基本数据类型
    java基础1。面向对象
    丑数
  • 原文地址:https://www.cnblogs.com/silenty/p/9351351.html
Copyright © 2011-2022 走看看