bzoj2286

虚树


如何构建虚树:
step1:将询问点按dfs序排好,root节点作为非关键点先入栈
step2:判断当前要加的点a[i]和sta[top]的关系
            我们令lc=lca(a[i],sta[top]);
            (1).如果lc在栈sta中并且就是栈顶的话,直接将a[i]加入栈
            (2).如果lc在栈sta中但是不是栈顶的话,一直弹栈到lc(lc不弹),且在弹栈的过程中加虚树边{sta[top-1],sta[top],w},然后将a[i]加入栈
            (3).lc不在栈sta中,弹栈一直弹到dfn[sta[top-1]]<dfn[lc],弹栈的过程中同样加虚树边{sta[top-1],sta[top],w},最后在加上{lc,sta[top],w}这条边,再将sta[top]弹出,lc,a[i]依次进栈
step3:在虚树上进行树形dp
 

注意每次要清空当前虚树的所有边(写的时候傻了for到n去清空了st,查了2小时)

/**************************************************************

    Problem: 2286

    User: syh0313

    Language: C++

    Result: Accepted

    Time:11004 ms

    Memory:45484 kb

****************************************************************/

 

#include <iostream>

#include <cstdio>

#include <cstdlib>

#include <cmath>

#include <algorithm>

#include <cstring>

using namespace std;

const int maxn=250010;

int n,m,st[maxn<<1],nt[maxn<<1],to[maxn<<1],topt,dfn[maxn],dfn_num,a[maxn],sta[maxn],top;

int fa[22][maxn],lg[maxn],dep[maxn],key[maxn],q[maxn],cnt;

long long w[maxn<<1],dis[maxn],mi[maxn],dp[maxn];

bool f[maxn];

inline void add(int x,int y,long long z)

{to[++topt]=y; nt[topt]=st[x]; st[x]=topt; w[topt]=z;}

void dfs(int x)

{

    f[x]=1; dfn[x]=++dfn_num; int p=st[x];

    while (p)

    {

        if (!f[to[p]])

        {

            dis[to[p]]=dis[x]+w[p]; dep[to[p]]=dep[x]+1;

            fa[0][to[p]]=x; mi[to[p]]=min(mi[x],w[p]); dfs(to[p]);

        }

        p=nt[p];

    }

}

inline void init()

{

    for (register int i=1;i<=maxn-10;i++) lg[i]=lg[i-1]+((1ll<<(lg[i-1]+1))==i);

    for (register int i=1;i<=20;i++)

     for (register int j=1;j<=n;j++) fa[i][j]=fa[i-1][fa[i-1][j]];

}

int lca(int x,int y)

{

    if (dep[x]<dep[y]) swap(x,y);

    while (dep[x]>dep[y]) {x=fa[lg[dep[x]-dep[y]]][x];}

    if (x==y) return x;

    for (register int i=20;i>=0;i--) if (fa[i][x]!=fa[i][y]) {x=fa[i][x]; y=fa[i][y];}

return fa[0][x];   

}

inline bool cmp(int aa,int bb){return dfn[aa]<dfn[bb];}

void tree_dp(int x,int fa)

{

    long long now=0; int p=st[x];

    if (key[x]) {dp[x]=mi[x]; return;}

    while (p)

    {

        if (to[p]!=fa) {tree_dp(to[p],x); now+=dp[to[p]];}

        p=nt[p];

    }

    dp[x]=min(mi[x],now);

}

inline int read()

{

    int xx=0,ff=1; char c=getchar();

    while (c<'0' || c>'9') {if (c=='-') ff=-1; c=getchar();}

    while (c>='0' && c<='9') {xx=(xx<<1)+(xx<<3)+c-'0'; c=getchar();}

return xx*ff;

}

inline long long readl()

{

    long long xx=0,ff=1; char c=getchar();

    while (c<'0' || c>'9') {if (c=='-') ff=-1; c=getchar();}

    while (c>='0' && c<='9') {xx=(xx<<1)+(xx<<3)+c-'0'; c=getchar();}

return xx*ff;

}

int main()

{

    n=read(); for (register int i=0;i<=n;i++) mi[i]=1e18,dp[i]=1e18;

    for (register int i=1;i<n;i++)

    {

        int xx,yy; long long zz; xx=read(); yy=read(); zz=readl();

        add(xx,yy,zz); add(yy,xx,zz);  

    }

    dfs(1); init(); m=read(); memset(f,0,sizeof f); memset(st,0,sizeof st);

    while (m--)

    {

        topt=0; top=0; cnt=0; int num; num=read();

        for (register int i=1;i<=num;i++) a[i]=read(),key[a[i]]=1;

        a[++num]=1; sort(a+1,a+num+1,cmp); sta[++top]=a[1]; f[a[1]]=1;

        for (register int i=2;i<=num;i++)

        {

            int lc=lca(sta[top],a[i]); q[++cnt]=lc;

            if (f[lc])

            {

                while (top && sta[top]!=lc)

                {

                    add(sta[top-1],sta[top],dis[sta[top]]-dis[sta[top-1]]);

                    f[sta[top]]=0; top--;

                }

                sta[++top]=a[i]; f[a[i]]=1;

            }      

            else

            {

                while (top>=2 && dfn[sta[top]]>dfn[lc] && dfn[sta[top-1]]>dfn[lc])

                {

                    add(sta[top-1],sta[top],dis[sta[top]]-dis[sta[top-1]]);

                    f[sta[top]]=0; top--; lc=lca(a[i],sta[top]);

                }

                add(lc,sta[top],dis[sta[top]]-dis[lc]); f[sta[top]]=0; top--;

                sta[++top]=lc; f[lc]=1; sta[++top]=a[i]; f[a[i]]=1;

            }

        }

        while (top)

        {

            if (top>=2) add(sta[top-1],sta[top],dis[sta[top]]-dis[sta[top-1]]);

            f[sta[top]]=0; top--;

        }

        tree_dp(1,0); printf("%lld\n",dp[1]);

        for (register int i=1;i<=num;i++) dp[a[i]]=1e18,key[a[i]]=0,f[a[i]]=0,st[a[i]]=0;

        for (register int i=1;i<=cnt;i++) st[q[i]]=0;

    }

return 0;

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值