[SDOI2011] bzoj 2286 消耗战 - 虚树dp - 虚树学习笔记

64 篇文章 0 订阅
40 篇文章 0 订阅

网上写的都看不懂,自我感觉就是:把关键点排序,然后一开始根节是一条链,然后依次加入每一个点,每次用当前的路径,把求出新加入的点到根的路径,减去,也就是只保留那些从根到当前这个点的路径的点,可以想象是用新加入的路径切掉之前路径,这个题建完虚树随便做。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#define QwQ printf("FAKE\n");
#define N 250010
#define M N<<1
#define LOG 20
#define lint long long
#define INF (LLONG_MAX/100LL-100LL)
#define inf (INT_MAX/10-10)
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
int stc[N],d[N],up[N][LOG],minv[N][LOG],in[N],a[N];
int list[N],val[N],Log[N];lint f[N];bool mark[N];
struct edges{
    int to,pre,w;
}e[M];int h[N],etop,cnt,dfs_clock,top;
inline int add_edge(int u,int v,int w)
{
//  debug(u)sp,debug(v)sp,debug(w)sp,debug(etop)sp,debug(h[u])ln;
    return e[++etop].to=v,e[etop].w=w,e[etop].pre=h[u],h[u]=etop;
}
int dfs(int x,int fa)
{
    d[x]=d[fa]+1,in[x]=++dfs_clock;
    for(int i=1;i<=Log[d[x]];i++)
        up[x][i]=up[up[x][i-1]][i-1],
        minv[x][i]=min(minv[x][i-1],minv[up[x][i-1]][i-1]);
    for(int i=h[x],y;i;i=e[i].pre)
        if((y=e[i].to)^fa) up[y][0]=x,minv[y][0]=e[i].w,dfs(y,x);
    return 0;
}
inline int getLCA(int x,int y,int &w1,int &w2)
{
    bool f=false;w1=w2=inf;
    if(d[x]<d[y]) swap(x,y),f=true;
    for(int i=Log[d[x]];i>=0;i--)
        if(d[up[x][i]]>=d[y])
            w1=min(w1,minv[x][i]),x=up[x][i];
    if(x==y) return (x+y)>>1;
    for(int i=Log[d[x]];i>=0;i--)
        if(up[x][i]^up[y][i])
            w1=min(w1,minv[x][i]),
            w2=min(w2,minv[y][i]),
            x=up[x][i],y=up[y][i];
    w1=min(w1,minv[x][0]),
    w2=min(w2,minv[y][0]),
    x=up[x][0],y=up[y][0];
    if(f) swap(w1,w2);return x;
}
inline int build_virtual(int *a,int n)//after sorting
{
    etop=d[stc[val[list[cnt=stc[top=1]=1]=1]=0]=0]=0;
//  for(int i=1;i<=n;i++) debug(i)sp,debug(a[i])ln;
    for(int i=1;i<=n;i++)
    {
        int x=a[i],y=stc[top],z=stc[top-1],w1=0,w2=0,c;
        while(d[c=getLCA(x,y,w1,w2)]<=d[z])
//          debug(x)sp,debug(y)sp,debug(c)sp,debug(z)sp,debug(d[c])sp,debug(d[z])ln,
            add_edge(z,y,val[top--]),y=z,z=stc[top-1];
        if(c^y) add_edge(c,y,w2),stc[top]=c,getLCA(c,z,val[top],w2),list[++cnt]=c;
        stc[++top]=x,val[top]=w1,list[++cnt]=x;
    }
    while(top>1) add_edge(stc[top-1],stc[top],val[top]),top--;
    return 0;
}
inline lint get_ans(int x)
{
    if(mark[x]) return f[x]=INF;
    for(int i=h[x];i;i=e[i].pre)
        f[x]+=min(get_ans(e[i].to),(lint)e[i].w);
    return f[x];
}
inline bool cmp(int a,int b)
{
    return in[a]<in[b];
}
int main()
{
    int n;scanf("%d",&n);
    for(int i=2;i<=n;i++) Log[i]=Log[i>>1]+1;
    for(int i=1,ct;i<n;i++)
    {
        int u,v;scanf("%d%d%d",&u,&v,&ct);
        add_edge(u,v,ct),add_edge(v,u,ct);
    }
    dfs(1,0);int m;scanf("%d",&m);
    memset(h,0,sizeof(h));
/*  for(int i=1;i<=n;i++)
    {
        debug(i)sp,debug(d[i])sp,debug(in[i])ln;
        for(int j=0;j<=Log[d[i]];j++)
            debug(j)sp,debug(up[i][j])sp,debug(minv[i][j])ln;
        cerr ln;
    }
    while(1)
    {
        int x,y,w1,w2,c;cin>>x>>y;
        c=getLCA(x,y,w1,w2);
        debug(x)sp,debug(y)sp,debug(c)sp,debug(w1)sp,debug(w2)ln;
    }*/
    while(m--)
    {
        int k;scanf("%d",&k);
        for(int i=1;i<=k;i++)
            scanf("%d",&a[i]),mark[a[i]]=true;
        sort(a+1,a+k+1,cmp);
        build_virtual(a,k),printf("%lld\n",get_ans(1));
        for(int i=1,x;i<=cnt;i++) h[x=list[i]]=0,f[x]=0ll;
        for(int i=1;i<=k;i++) mark[a[i]]=false;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值