bzoj3572: [Hnoi2014]世界树

题目链接

bzoj3572: [Hnoi2014]世界树

题解

构建虚树
对于虚树dp
我们首先对于虚中的点,处理处每个点属于哪个点管辖,这个两边dp就好了,一次用儿子更新父亲,一次父亲更新儿子
然后用虚树中的点扩展到整棵树,考虑
若叙述中相邻两点归属于同一个点管辖,那么他们中间的点也肯定都归该点管辖
若不同,在这两点间的链上一定存在一个点将两部分分开,我们倍增将这个点求出
我是不会告诉你们这样写add(stack[top - 1],stack[top--]);会GG的,QAQ
要add(stack[top - 1],stack[top]);top--;好坑啊,调了一天
辣鸡虚树,毁我青春Qwq

/

#include<cstdio> 
#include<cstring> 
#include<algorithm> 
inline int read() { 
	int x = 0,f = 1; 
	char c = getchar(); 
	while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar(); } 
	while(c <= '9' && c >= '0')x = x * 10 + c - '0',c = getchar(); 
	return x * f; 
} 
const int maxn = 300007; 
int n; 
struct node { 
	int v,next; 
} edge[maxn << 1],e[maxn << 1];   
int head[maxn],num,h[maxn],num1,deep[maxn];  
inline void add(int u,int v) { if(u == v) return; 
	e[++ num1].v = v;e[num1].next = h[u];h[u] = num1; 
} 
inline void add_edge(int u,int v ) { 
	edge[++ num].v = v;edge[num].next = head[u];head[u] = num; 
} 

int dfn[maxn],cnt = 0,dad[maxn][20],size[maxn]; 

void dfs(int x,int fa) { 
	size[x] = 1; 
 	dfn[x] = ++ cnt; deep[x] = deep[fa] + 1; dad[x][0] = fa; 
	for(int i = 0;dad[x][i];++ i) 
		dad[x][i + 1] = dad[dad[x][i]][i]; 
	for(int i = head[x];i;i = edge[i].next ) { 
		 int v = edge[i].v; 
		 if(v == fa) continue; 
		 dfs(v,x); 
		size[x] += size[v]; 
	} 
} 
int lca(int x,int y) {
	if(deep[x] > deep[y]) std::swap(x,y); 
	for(int i = 18;i >= 0;-- i)if(deep[x] <= deep[dad[y][i]]) y = dad[y][i]; 
       	if(x == y) return x; 
	for(int i = 18;i >= 0;-- i) if(dad[x][i] != dad[y][i])x = dad[x][i],y = dad[y][i]; 	
	return x == y ? x : dad[x][0]; 
} 

inline bool cmp(int x,int y) {return dfn[x] < dfn[y];} 
int c[maxn],rem[maxn],Dfn,bel[maxn];  

int dis(int x,int y) { 
	return deep[x] + deep[y] - 2 * deep[lca(x,y)]; 
} 

void get_fri(int x) { 
	c[++ Dfn] = x;rem[x] = size[x];  
	for(int i = h[x];i;i = e[i].next ) { 
		int v = e[i].v; 
		get_fri(v); 
		if(!bel[v]) continue; 
		int t1 = dis(bel[v],x),t2 = dis(bel[x],x); 
		if((t1 == t2 && bel[v] < bel[x]) || t1 < t2 || !bel[x]) bel[x] = bel[v]; 
	} 
} 	
void get_sec(int x) { 
	for(int i = h[x];i;i = e[i].next) { 
		int v = e[i].v; 
		int t1 = dis(bel[x],v),t2 = dis(bel[v],v); 
		if((t1 == t2 && bel[v] > bel[x]) || t1 < t2 || !bel[v])bel[v] = bel[x]; 
		get_sec(v); 
	} 		
} 

int dp[maxn]; 
void divide(int a,int b) { 
	int x = b,mid = b; 
	for(int i = 18;i >= 0;-- i) 
		if(deep[dad[x][i]] > deep[a]) x = dad[x][i]; 
	rem[a] -= size[x]; 
	if(bel[a] == bel[b]) {
		dp[bel[a]] += size[x] - size[b]; return; 
	}  
	for(int i = 18;i >= 0;-- i) { 
		if(deep[dad[mid][i]] <= deep[a]) continue; 
		int t1 = dis(bel[a],dad[mid][i]),t2 = dis(bel[b],dad[mid][i]); 
		if(t1 > t2 || (t1 == t2 && bel[b] < bel[a]))mid = dad[mid][i]; 
	} 
	dp[bel[a]] += size[x] - size[mid]; 
	dp[bel[b]] += size[mid] - size[b]; 
} 
int htmp[maxn],stack[maxn],H[maxn]; 
void solve(int k = 0) { 
	int top;memset(h,0,sizeof h);
	top = Dfn = 0;
	k = read(); 
	for(int i = 1;i <= k;++ i) htmp[i] = H[i] = read(); 
	for(int i = 1;i <= k;++ i) bel[H[i]] = H[i] ; 
	std::sort(H + 1,H + k + 1,cmp); 
	stack[++ top] = 1; 
	for(int i = 1;i <= k;++ i) { 
		int x = H[i],f = lca(stack[top],x); 
		while("tle") { 
			if(deep[f] >= deep[stack[top - 1]]) { 
				add(f,stack[top --]); 
				if(stack[top] != f) stack[++ top] = f; 
				break; 	
			}  add(stack[top - 1],stack[top]);top --;  
		}  if(stack[top] != x) stack[++ top] = x; 	
	} 
	while(top - 1) add(stack[top - 1],stack[top]),top --; 
	get_fri(1); 
	get_sec(1); 
	for(int i = 1;i <= Dfn;++ i)  
		for(int j = h[c[i]];j;j = e[j].next) { 
	       		divide(c[i],e[j].v); 		
		} 
	for(int i = 1;i <= Dfn;++ i) dp[bel[c[i]]] += rem[c[i]]; 
       	for(int i = 1;i <= k;++ i) printf("%d ",dp[htmp[i]]);puts(""); 
	for(int i = 1;i <= Dfn;++ i) dp[c[i]] = bel[c[i]] = H[c[i]] = rem[c[i]] = 0; 
	num1 = 0; 
} 
int main() { 
	n = read(); 
	for(int u,v,i = 1;i < n;++ i) { 
		u = read(),v = read(); 
		add_edge(u,v);add_edge(v,u); 
	} 
	dfs(1,0);	
	int q = read() ; 
	for(;q --;)  
	solve(); 
	return 0; 
} 
 

posted @ 2018-06-27 19:53  zzzzx  阅读(205)  评论(2编辑  收藏  举报