zoukankan      html  css  js  c++  java
  • bzoj3611: [Heoi2014]大工程

    思路:首先因为n的范围很大,考虑建立虚树,就是把大部分的冗余的点去掉然后建立一颗新的树然后再树形dp,具体实现可以考虑用一个栈维护,首先求出每个点的dfn然后按照dfn排序,然后用一个栈维护(栈里存的关键点或关键点之间的lca或关键点lca的lca等等。。。),然后枚举关键点,求出关键点与当前栈顶元素的lca,如果当前栈顶元素就是当前关键点的父亲(也就是他们的lca就是栈顶元素),直接就把当前关键点push进栈,否则就可以直接连边了,将stack[top-1]向stack[top]连边,因为此时lca一定是stack[top]的祖先,这样连边显然没有问题,当然要判一下dfn[stack[top-1]]是否大于等于dfn[lca],因为只有stack[top-1]也是lca的后代时才能连边,如果dfn[stack[top-1]]<dfn[lca]说明stack[top-1]为lca的祖先(是祖先不能是兄弟,因为如果是兄弟一定会在处理当前栈顶元素时就已经弹出栈了),这时显然就不能连边了,然后把lca也push进栈就行了(记得判一下可能lca已经在栈中,对应的就是dfn[stack[top-1]]==dfn[lca]),其本质也就是维护最右边的那条链,一旦有点不属于最右边的那条链直接连边就好了,最后栈中剩下的元素也一定是最右边的那条链中的元素,直接连边就好了。

    然后就tree dp就好了。我写的比较丑,多维护了很多东西,凑合着看吧。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    #define maxn 1000005
    #define inf 1e9
    
    int n,q,tot,top,ans1,ans2,deg,l;
    int now[maxn],pre[2*maxn],son[2*maxn],dep[maxn],stack[maxn],a[maxn];
    int maxdis[maxn],mindis[maxn],size[maxn],dfn[maxn];
    long long sum[maxn],sumdep[maxn];
    int f[maxn][21];
    bool bo[maxn];
    
    inline int read(){
    	int x=0;char ch=getchar();
    	for (;ch<'0'||ch>'9';ch=getchar());
    	for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    	return x;
    }
    
    void add(int a,int b){
    	son[++tot]=b;
    	pre[tot]=now[a];
    	now[a]=tot;
    }
    
    void link(int a,int b){
    	add(a,b),add(b,a);
    }
    
    void dfs(int x,int fa){
    	dep[x]=dep[fa]+1,dfn[x]=++deg;
    	for (int i=1;i<=l;i++) f[x][i]=f[f[x][i-1]][i-1];
    	for (int p=now[x];p;p=pre[p])
    		if (son[p]!=fa) f[son[p]][0]=x,dfs(son[p],x);
    }
    
    int lca(int a,int b){
    	if (dep[a]<dep[b]) swap(a,b);int x=dep[a]-dep[b],t=0;
    	for (;x;x>>=1,t++) if (x&1) a=f[a][t];t=l;
    	if (a==b) return a;
    	for (;f[a][0]!=f[b][0];){
    		for (;f[a][t]==f[b][t];t--);
    		a=f[a][t],b=f[b][t];
    	}
    	return f[a][0];
    }
    
    bool cmp(int a,int b){return dfn[a]<dfn[b];}
    
    void tree_dp(int x){
    	long long s=0;
    	mindis[x]=inf,size[x]=0,sum[x]=maxdis[x]=0,sumdep[x]=0;
    	for (int p=now[x];p;p=pre[p]){
    		tree_dp(son[p]);int d=dep[son[p]]-dep[x];
    		if (bo[x]) sum[x]+=sumdep[son[p]]+size[son[p]]*d;
    		sum[x]+=sum[son[p]]+s*size[son[p]]+(sumdep[son[p]]+1ll*size[son[p]]*d)*size[x];
    		ans1=min(ans1,mindis[x]+mindis[son[p]]+d);
    		ans2=max(ans2,maxdis[x]+maxdis[son[p]]+d);
    		mindis[x]=min(mindis[x],mindis[son[p]]+d);
    		maxdis[x]=max(maxdis[x],maxdis[son[p]]+d);
    		s+=sumdep[son[p]]+1ll*d*size[son[p]],size[x]+=size[son[p]];
    	}
    	size[x]+=bo[x],sumdep[x]=s;
    	if (bo[x]) ans1=min(ans1,mindis[x]),ans2=max(ans2,maxdis[x]),mindis[x]=0;
    	now[x]=0;
    }
    
    int main(){
    	n=read();l=log2(n);
    	for (int i=1,u,v;i<n;i++) u=read(),v=read(),link(u,v);
    	dfs(1,0);memset(now,0,sizeof(now)),tot=0;
    	q=read();
    	while (q--){
    		n=read();for (int i=1;i<=n;i++) a[i]=read(),bo[a[i]]=1;
    		top=0,ans1=inf,ans2=0;sort(a+1,a+n+1,cmp);
    		for (int i=1;i<=n;i++){
    			if (!top){stack[++top]=a[i];continue;}
    			int x=lca(stack[top],a[i]);
    			for (;dfn[x]<dfn[stack[top]];){
    				if (dfn[x]>=dfn[stack[top-1]]){
    					add(x,stack[top]);
    					if (stack[--top]!=x) stack[++top]=x;
    					break;
    				}
    				add(stack[top-1],stack[top]),top--;
    			}
    			stack[++top]=a[i];
    		}
    		while (top>1) add(stack[top-1],stack[top]),top--;
    		tree_dp(stack[1]);
    		printf("%lld %d %d
    ",sum[stack[1]],ans1,ans2);
    		for (int i=1;i<=n;i++) bo[a[i]]=0;tot=0;
    	}
    	return 0;
    }
    
  • 相关阅读:
    Linux下nginx 的常用命令
    linux下nginx常用命令
    docker logs 命令使用
    docker pause 命令使用
    容器到底是个啥?(附Docker学习资源汇总)
    Docker镜像(image)详解
    Docker常用命令
    Docker常用命令大全
    NEW SECRET “???” ACHIEVEMENT AND CARD BACK FROM BARRENS – MYSTERIES OF THE PHOENIX – FULL SOLUTION INSIDE!
    凤凰之谜卡背 new card back "secrets of the phoenix"
  • 原文地址:https://www.cnblogs.com/DUXT/p/5992149.html
Copyright © 2011-2022 走看看