Bzoj3611大工程(虚树+DP)

Posted void_f

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Bzoj3611大工程(虚树+DP)相关的知识,希望对你有一定的参考价值。

Description

题目链接

Solution

在虚树上跑DP即可

关于虚树的建立,是维护一个最右链的过程

关键代码如下:

sort(A+1,A+k+1,cmp);//按dfs序排序
s[top=1]=1;//栈维护最右链
for(int i=1;i<=k;++i){
    int t=A[i],f=0;
    while(top){
        f=LCA(A[i],s[top]);
        if(top>1&&dep[f]<dep[s[top-1]])
            Link(s[top-1],s[top]),top--;
        else if(dep[f]<dep[s[top]]){Link(f,s[top--]);break;}
        else break;
    }
    if(s[top]!=f) s[++top]=f;
    s[++top]=t;
}
while(--top) Link(s[top],s[top+1]);

Code

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define Inf 0x7fffffff
#define ll long long
#define N 1000010
using namespace std;

struct info{int to,nex,w;}e[N*4];
int n,tot,head[N],dfn[N],fa[N][20],_log,dep[N],A[N],mn[N],mx[N];
bool b[N];
ll Ans1,Ans2,f[N],size[N],sum;

inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

inline void Link(int u,int v){
    if(u==v) return;
    e[++tot].to=v;e[tot].nex=head[u];head[u]=tot;e[tot].w=dep[v]-dep[u];
}

void dfs(int u,int pre){
    dfn[u]=++tot;
    for(int i=1;i<=_log;++i) 
        fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int i=head[u];i;i=e[i].nex){
        int v=e[i].to;
        if(v==pre) continue;
        dep[v]=dep[u]+1;
        fa[v][0]=u;
        dfs(v,u);
    }
    head[u]=0;
}

int LCA(int u,int v){
    if(dep[u]>dep[v]) swap(u,v);
    int d=dep[v]-dep[u];
    
    for(int i=0;i<=_log;++i)
        if(d&(1<<i)) v=fa[v][i];
    if(u==v) return v;
    
    for(int i=_log;i>=0;--i)
        if(fa[u][i]!=fa[v][i]){
            u=fa[u][i];
            v=fa[v][i];
        }
    return fa[u][0];
}


bool cmp(int a,int b){return dfn[a]<dfn[b];}
int s[N],top;
void bulid(){
    int k=read();for(int i=1;i<=k;++i) b[A[i]=read()]=1;
    sort(A+1,A+k+1,cmp);
    s[top=1]=1;
    for(int i=1;i<=k;++i){
        int t=A[i],f=0;
        while(top){
            f=LCA(A[i],s[top]);
            if(top>1&&dep[f]<dep[s[top-1]])
                Link(s[top-1],s[top]),top--;
            else if(dep[f]<dep[s[top]]){Link(f,s[top--]);break;}
            else break;
        }
        if(s[top]!=f) s[++top]=f;
        s[++top]=t;
    }
    while(--top) Link(s[top],s[top+1]);
}

void dp(int u){
    size[u]=b[u];
    f[u]=0;
    mn[u]=b[u]?0:Inf;
    mx[u]=b[u]?0:-Inf;
    for(int i=head[u];i;i=e[i].nex){
        int v=e[i].to;
        dp(v);
        sum+=(f[u]+size[u]*e[i].w)*size[v]+f[v]*size[u];
        size[u]+=size[v];
        f[u]+=f[v]+e[i].w*size[v];
        Ans1=min(Ans1,(ll)mn[u]+mn[v]+e[i].w);
        Ans2=max(Ans2,(ll)mx[u]+mx[v]+e[i].w);
        mn[u]=min(mn[u],mn[v]+e[i].w);
        mx[u]=max(mx[u],mx[v]+e[i].w);
    }
    head[u]=0;
}

int main(){
    n=read();_log=log(n)/log(2);
    for(int i=1;i<n;++i){
        int u=read(),v=read();
        Link(u,v);Link(v,u);
    }
    tot=0;dfs(1,0);
    int q=read();
    while(q--){
        bulid();
        Ans1=1e16,Ans2=-1e16,sum=0;
        dp(1);
        printf("%lld %lld %lld\n",sum,Ans1,Ans2);
        memset(b,0,sizeof(b));
    }
    return 0;
}

以上是关于Bzoj3611大工程(虚树+DP)的主要内容,如果未能解决你的问题,请参考以下文章

虚树(Bzoj3611: [Heoi2014]大工程)

[bzoj3611][Heoi2014]大工程

[Bzoj3611][Heoi2014]大工程(虚树)

BZOJ3611大工程(虚树,动态规划)

bzoj3611 [Heoi2014]大工程

BZOJ3611 [Heoi2014]大工程 虚树