【BZOJ】3611: [Heoi2014]大工程-虚树&树形DP

传送门:bzoj3611题解C(k,2)条边就是把所有点之间都连起来…然后询问所有两两之间边的最大值和最小值。(读错题意了蜜汁尴尬) 注意询问节点总数不超过2n,当然是建虚树,然后在虚树上dp啦。 建虚树的方法是这样的,要维护一条最右链(用的链式前向星,当然是最右了(雾)) k=rd();for(i=1;i<=k;++i) {a[i]=rd();in[a...
摘要由CSDN通过智能技术生成

传送门:bzoj3611


题解

C(k,2)条边就是把所有点之间都连起来…然后询问所有两两之间边的最大值和最小值。(读错题意了蜜汁尴尬)
注意询问节点总数不超过2n,当然是建虚树,然后在虚树上dp啦。
建虚树的方法是这样的,要维护一条最右链(用的链式前向星,当然是最右了(雾))

        k=rd();for(i=1;i<=k;++i) {a[i]=rd();in[a[i]]=1;}
        sort(a+1,a+k+1,cmp);//按dfs序排序
        sta[top=1]=1;
        for(i=1;i<=k;++i){
            t=LCA(a[i],sta[top]);
            while(d[t]<d[sta[top]]){
                if(d[t]>=d[sta[top-1]]){
                    lk(t,sta[top--]);
                    if(sta[top]!=t) sta[++top]=t;
                    break;
                }
                lk(sta[top-1],sta[top]);top--;
            }
            if(sta[top]!=a[i]) sta[++top]=a[i];
        }
        for(;top>1;top--) lk(sta[top-1],sta[top]);//AddEdge

dp过程详见代码(懒)


代码

#include<cstdio>
#include<algorithm>
#include<cctype>
using namespace std;
typedef long long ll;
const int N=2e6+50,inf=2e9;
int n,T,k,F[N][22],bin[22],MN,MX,df[N],d[N],a[N],dfn;
int fir[N],dir[N<<1],next[N<<1],nt,in[N],sta[N],top;
int head[N],to[N],nxt[N],w[N],mi[N],mx[N],sz[N],tot;
ll ans,sum[N];
char *TT,*mo,but[(1<<15)+2];
#define getchar() ((TT==mo && (mo=(TT=but)+fread(but,1,1<<15,stdin)),TT==mo)?0:*TT++)
inline int rd()
{
    register char ch=getchar();register int x=0,f=1;
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
    return x*f;
}
inline bool cmp(const int&x,const int&y){return df[x]<df[y];}
inline void lkk(int u,int v)
{dir[++nt]=v;next[nt]=fir[u];fir[u]=nt;}
inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=d[v]-d[u];}

inline int LCA(int x,int y)
{
    if(d[y]>d[x]) swap(x,y);
    register int t=d[x]-d[y],i;
    for(i=0;bin[i]<=t;++i) if(bin[i]&t) x=F[x][i];
    for(i=20;i>=0 && x!=y;--i)
     if(F[x][i]!=F[y][i])
      x=F[x][i],y=F[y][i];
    return x==y?x:F[x][0];
}

inline void dfs(int x)
{
    register int i,j;df[x]=++dfn;
    for(i=1;bin[i]<=d[x];++i) F[x][i]=F[F[x][i-1]][i-1];
    for(i=fir[x];i;i=next[i]){
        if((j=dir[i])==F[x][0]) continue;
        d[j]=d[x]+1;F[j][0]=x;
        dfs(j);
    }
}

inline void dfss(int x)
{
    sz[x]=in[x];sum[x]=0;
    mx[x]=in[x]? 0:-inf;mi[x]=in[x]? 0:inf;
    for(register int j,i=head[x];i;i=nxt[i]){
        j=to[i];dfss(j);
        ans+=sum[j]*sz[x]+1ll*(sum[x]+1ll*w[i]*sz[x])*sz[j];
        sz[x]+=sz[j];sum[x]+=sum[j]+1ll*sz[j]*w[i];
        MN=min(MN,mi[x]+mi[j]+w[i]);
        MX=max(MX,mx[x]+mx[j]+w[i]);
        mi[x]=min(mi[x],w[i]+mi[j]);
        mx[x]=max(mx[x],w[i]+mx[j]);
    }
    head[x]=0;
}

int main(){
    register int i,j,ix,iy,t;
    bin[0]=1;for(i=1;i<=20;++i) bin[i]=bin[i-1]<<1;
    n=rd();
    for(i=1;i<n;++i) {ix=rd();iy=rd();lkk(ix,iy);lkk(iy,ix);}
    dfs(1); 
    T=rd();
    while(T--){
        ans=0;tot=0;MX=-inf;MN=inf;
        k=rd();for(i=1;i<=k;++i) {a[i]=rd();in[a[i]]=1;}
        sort(a+1,a+k+1,cmp);
        sta[top=1]=1;
        for(i=1;i<=k;++i){
            t=LCA(a[i],sta[top]);
            while(d[t]<d[sta[top]]){
                if(d[t]>=d[sta[top-1]]){
                    lk(t,sta[top--]);
                    if(sta[top]!=t) sta[++top]=t;
                    break;
                }
                lk(sta[top-1],sta[top]);top--;
            }
            if(sta[top]!=a[i]) sta[++top]=a[i];
        }
        for(;top>1;top--) lk(sta[top-1],sta[top]);
        dfss(1);
        printf("%lld %d %d\n",ans,MN,MX);
        for(i=1;i<=k;++i) in[a[i]]=0;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值