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\n",sum[stack[1]],ans1,ans2);
		for (int i=1;i<=n;i++) bo[a[i]]=0;tot=0;
	}
	return 0;
}

 

posted @ 2016-10-24 10:55  DUXT  阅读(143)  评论(0编辑  收藏  举报