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; }