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;
    }
    
  • 相关阅读:
    宿主机无法访问CentOS7上Jenkins服务的解决办法
    415. Add Strings
    367. Valid Perfect Square
    326. Power of Three
    258. Add Digits
    231. Power of Two
    204. Count Primes
    202. Happy Number
    172. Factorial Trailing Zeroes
    171. Excel Sheet Column Number
  • 原文地址:https://www.cnblogs.com/DUXT/p/5992149.html
Copyright © 2011-2022 走看看