• 虚树略解


    虚树

    对树上一类问题的处理

    这类问题中,询问的点数远远小于树的点数,有些边可以一起统计

    那我们只保留需要用的点就可以了

    明显是关键点以及拐点(都是某些LCA)

    考虑把每个点按照dfs序排序,按顺序求得两两lca并且去重就能求出虚树上的所有点

    构建

    具体构建我们用栈实现,栈里面的元素按dfs序单调递增

    按dfs序访问所有关键点,对于每个要加入的点 (p),有两种情况:

    1. (p) 和栈顶元素 (x) 的lca是(x),说明 (p)(x) 子树里面,将其入栈
    2. 否则栈顶元素的子树一定访问完毕(dfs序),考虑构建

    设栈顶元素为(x),第二个元素为(y)

    • (dfn[y]>dfn[lca]),可以连边(y) −> (x) ,将 (x) 出栈;
    • (dfn[y]=dfn[lca]),即(y=lca),连边(lca−>x),此时 (lca(y)) 的子树构建完毕;
    • (dfn[y]<dfn[lca]),即(lca)(y,x)之间,连边(lca−>x)(x)出栈,再将(lca)入栈。此时lca的子树构建完毕(break)

    代码:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define ll long long
    const int N = 1000010,M=2000010,K = 23,inf=192608170;
    using namespace std;
    int n;
    inline int read(){
    	int x=0,pos=1;char ch=getchar();
    	for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
    	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
    	return pos?x:-x;
    }
    struct node{
    	int v,nex;
    };
    ll ans1,ans2,f[N],maxs[N],mins[M],sz[N]; 
    int fa[N][23],dfn[N],tim,bo[N],pi[N],st[N],dep[N],dis[N],num,bq[N];
    struct graph{
    	node edge[M];int top,head[N];
    	void add(int u,int v){
    		edge[++top].v=v;edge[top].nex=head[u];head[u]=top;
    	}
    	void dfs1(int x){
    		dfn[x]=++tim;
    		for(int i=1;i<=20;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
    		for(int i=head[x];i;i=edge[i].nex){
    			int v=edge[i].v;
    			if(v!=fa[x][0])
    				dis[v]=dis[x]+1,dep[v]=dep[x]+1,fa[v][0]=x,dfs1(v);
    		}
    	}
    	void dfs2(int x){
    		sz[x]=bo[x],maxs[x]=0,mins[x]=inf,f[x]=0;
    		for(int i=head[x];i;i=edge[i].nex){
    			int v=edge[i].v,d=dis[v]-dis[x];
    			dfs2(v);sz[x]+=sz[v];
    			ans1=min(ans1,mins[x]+mins[v]+d),ans2=max(ans2,maxs[x]+maxs[v]+d);
    			mins[x]=min(mins[x],mins[v]+d),maxs[x]=max(maxs[x],maxs[v]+d); 
    			f[x]+=f[v]+1ll*d*(num-sz[v])*sz[v];
    		}
    		if(bo[x]) ans1=min(ans1,mins[x]),ans2=max(ans2,maxs[x]),mins[x]=0;
    		head[x]=0;
    	}
    }g1,g2;
    int q;
    int cmp(int a,int b){
    	return dfn[a]<dfn[b];
    }
    int lca(int a,int b){
    	if(dep[a]<dep[b]) swap(a,b);
    	for(int h=dep[a]-dep[b],i=20;i>=0;i--){
    		if(h>=(1<<i)){
    			h-=(1<<i);
    			a=fa[a][i];
    		}
    	}
    	if(a==b) return a;
    	for(int i=20;i>=0;i--) if(fa[a][i]!=fa[b][i]) a=fa[a][i],b=fa[b][i];
    	return fa[a][0];
    }
    void work(){
    	sort(pi+1,pi+num+1,cmp);
    	int tp=0;
    	for(int i=1;i<=num;i++){
    		if(!tp){
    			st[++tp]=pi[i];continue;
    		}
    		int u=lca(st[tp],pi[i]);
    		while(dfn[u]<dfn[st[tp]]){
    			if(dfn[u]>=dfn[st[tp-1]]){
    				g2.add(u,st[tp]);
    				if(st[--tp]!=u) st[++tp]=u;
    				break;
     			}
     			g2.add(st[tp-1],st[tp]);tp--;
    		}
    		st[++tp]=pi[i];
    	}
    	while(tp>1) g2.add(st[tp-1],st[tp]),tp--;
    	ans1=inf,ans2=0,g2.dfs2(st[1]);
    	printf("%lld %d %d
    ",f[st[1]],ans1,ans2);
    	for(int i=1;i<=num;i++) bo[pi[i]]=0;
    	for(int i=1;i<=g2.top;i++) g2.edge[i].nex=g2.edge[i].v=0;g2.top=0;
    }
    int main(){
    	n=read();g1.top=g2.top=0;
    	for(int i=1,u,v;i<n;i++){
    		u=read(),v=read();
    		g1.add(u,v);g1.add(v,u); 
    	}
    	g1.dfs1(1);q=read();
    	for(int i=1;i<=q;i++){
    		num=read();
    		for(int j=1;j<=num;j++){
    			pi[j]=read();bo[pi[j]]=1; 
    		} 
    		work();
    	}
    	return 0;
    }
    
  • 相关阅读:
    bzoj 2456 mode
    codeforces 630 I(规律&&组合)
    codeforces 630H (组合数学)
    codeforces 651A Joysticks
    codeforces 651B Beautiful Paintings
    codeforces 625C K-special Tables
    codeforces 630F Selection of Personnel(组合数)
    codeforce 630N Forecast
    后缀数组模板
    Hdu5737-Differencia(有序表线段树)
  • 原文地址:https://www.cnblogs.com/lcyfrog/p/11586210.html
Copyright © 2020-2023  润新知