BZOJ 2286: [Sdoi2011]消耗战(虚树+树形dp)

传送门

解题思路

  看到\(\sum\limits k_i<=500000\),应该是虚树了。然后发现把虚树建好后就是个\(sb\)树形\(dp\)了。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>

using namespace std;
const int N=250010;
typedef long long LL;
const LL inf=1e18;

template<class T> void rd(T &x){
    x=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
}

int to[N<<1],nxt[N<<1],val[N<<1],a[N<<1],tot,dfn[N],num,tag[N];
int n,m,head[N],siz[N],top[N],son[N],fa[N];
int stk[N<<1],tp,cnt,to_[N],nxt_[N],head_[N],cnt_;
int g[N][25],zz[N][25],val_[N];
LL dep[N],f[N][3],ans;

inline void add(int bg,int ed,int z){
    to[++cnt]=ed,nxt[cnt]=head[bg],val[cnt]=z,head[bg]=cnt;
}   

inline void add_(int bg,int ed,int z){
//  cout<<bg<<" "<<ed<<" "<<z<<endl;
    to_[++cnt_]=ed,nxt_[cnt_]=head_[bg],head_[bg]=cnt_,val_[cnt_]=z;
}

void dfs1(int x,int F,LL d){
    dep[x]=d;siz[x]=1;fa[x]=F;dfn[x]=++num;
    int maxson=-1,u;zz[x][0]=F;
    for(int i=1;i<=20;i++){
        zz[x][i]=zz[zz[x][i-1]][i-1];
        g[x][i]=min(g[x][i-1],g[zz[x][i-1]][i-1]);
    }
    for(int i=head[x];i;i=nxt[i]){
        u=to[i];if(u==F) continue;g[u][0]=val[i];
        dfs1(u,x,d+val[i]);siz[x]+=siz[u];
        if(siz[u]>maxson) maxson=siz[u],son[x]=u;
    }
}

void dfs2(int x,int topf){
    top[x]=topf;if(!son[x]) return ;
    dfs2(son[x],topf);
    for(int i=head[x];i;i=nxt[i]){
        int u=to[i];if(u==fa[x] || u==son[x]) continue;
        dfs2(u,u);
    }
}

inline bool cmp(int x,int y){
    return dfn[x]<dfn[y];
}

inline int LCA(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]>=dep[top[y]]) x=fa[top[x]];
        else y=fa[top[y]];
    }
    return dep[x]>dep[y]?y:x;
}

inline int dis(int x,int y){
    int ret=1e9+1;
    for(int i=22;i>=0;i--){
        if(dep[zz[x][i]]<dep[y]) continue;
        if(!g[x][i]) continue;
        ret=min(ret,g[x][i]);
        x=zz[x][i];
    }
    return ret;
}

void dp(int x){
    f[x][0]=f[x][1]=0;
    for(int i=head_[x];i;i=nxt_[i]){
        int u=to_[i];dp(u);
        f[x][1]+=min(f[u][0],f[u][1]);
        f[x][0]+=min(f[u][0],f[u][1]+val_[i]);
    }
    if(tag[x]) f[x][0]=inf;tag[x]=0;
}

inline void work(){
    a[++tot]=1;sort(a+1,a+1+tot,cmp);int u=tot;
    for(int i=2;i<=tot;i++) a[++u]=LCA(a[i],a[i-1]);
    sort(a+1,a+1+u);tot=unique(a+1,a+1+u)-a-1;
    sort(a+1,a+1+tot,cmp);tp=0;
    stk[++tp]=a[1];cnt_=0;
    for(int i=2;i<=tot;i++){
        while(tp && LCA(stk[tp],a[i])!=stk[tp]) tp--;
        add_(stk[tp],a[i],dis(a[i],stk[tp]));
        stk[++tp]=a[i];
    }
    /*
    for(int i=1;i<=tot;i++){
        cout<<a[i]<<" :"<<endl;
        for(int j=head_[a[i]];j;j=nxt_[j]){
            cout<<to_[j]<<" ";
        }
        cout<<endl;
    }
    */
    dp(1);ans=f[1][0];
    for(int i=1;i<=tot;i++) head_[a[i]]=0;
}   

int main(){
    rd(n);int x,y,z;
    for(int i=1;i<n;i++){
        rd(x);rd(y);rd(z);
        add(x,y,z),add(y,x,z);
    }
    dfs1(1,0,0);dfs2(1,1);
    rd(m);
    while(m--){
        rd(tot);
        for(int i=1;i<=tot;i++) rd(a[i]),tag[a[i]]=1;
        work();
        printf("%lld\n",ans);
    }
    return 0;
}

转载于:https://www.cnblogs.com/sdfzsyq/p/10272933.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值