• 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; 
    } 
     
    
    
  • 相关阅读:
    阅读笔记--- 04
    站立会议--06个人进度
    站立会议--05 个人
    站立会议---04个人
    场景分析
    站立会议---03个人
    站立会议---02 个人进度
    计算某一天距离今天多少天,多少小时,多少分钟
    改变图片颜色
    手动调整导航控制器中的viewcontroller
  • 原文地址:https://www.cnblogs.com/sssy/p/9235747.html
Copyright © 2020-2023  润新知