1. 程式人生 > >洛谷2495 BZOJ2286 SDOI2011 消耗戰 虛樹 樹形dp

洛谷2495 BZOJ2286 SDOI2011 消耗戰 虛樹 樹形dp

題目連結

題意:
給你一棵以1為根的樹,邊有邊權,有m次詢問,每次詢問選出k個點,問這k個與1號點都不連通要割斷的最小邊權和。 n , m < = 1 e

5 n,m<=1e5 量級, k \sum k O (
n ) O(n)
量級的。

題解:
一道虛樹模板題。
這種樹上選若干個點的題基本就是往虛樹方面想了。我們預處理出每個點割斷它到1號點的最小代價,然後每次詢問建出虛樹。如果父節點要被割斷,那麼就沒有必要建它的子樹中被選中的節點了,於是我們在虛樹裡只需要考慮把每個葉子割斷的最小代價就好了。然後進行樹形dp, d

p [ x ] dp[x] 表示割斷 x x 所在的所有葉子花費的最小代價,有 d p [ x ] = m i n ( y s o n [ x ] d p [ y ] , x ) dp[x]=min(\sum_{y\in son[x]}dp[y],x連向父節點的邊的權值) 。這樣我們就能做到 O ( n l o g n ) O(nlogn) 的複雜度了。
注意開long long。

程式碼:

#include <bits/stdc++.h>
using namespace std;

int n,m,hed[1800010],cnt,f[800010][21],dep[1800010],z;
int sta[1800010],dfn[1800010],k,b[1800010],tp;
long long mn[800010];
struct node
{
    int to,next;
    long long dis;
}a[1800010],aa[1800010];
vector<int> v[1800010];
inline void add(int from,int to,long long dis)
{
    a[++cnt].to=to;
    a[cnt].dis=dis;
    a[cnt].next=hed[from];
    hed[from]=cnt;
}
inline void dfs(int x)
{
    dfn[x]=++z;
    for(int i=1;i<=20;++i)
    f[x][i]=f[f[x][i-1]][i-1];
    for(int i=hed[x];i;i=a[i].next)
    {
        int y=a[i].to;
        if(y==f[x][0])
        continue;
        f[y][0]=x;
        dep[y]=dep[x]+1;
        mn[y]=min(mn[x],a[i].dis);
        dfs(y); 
    }
}
inline int cmp(int x,int y)
{
    return dfn[x]<dfn[y];
}
inline int lca(int x,int y)
{
    if(dep[x]<dep[y])
    swap(x,y);
    for(int i=20;i>=0;--i)
    {
        if(dep[x]-dep[y]>=(1<<i))
        x=f[x][i];
    }
    if(x==y)
    return x;
    for(int i=20;i>=0;--i)
    {
        if(f[x][i]!=f[y][i]&&dep[x]>=(1<<i))
        {
            x=f[x][i];
            y=f[y][i];
        }
    }
    return f[x][0];
}
inline void add2(int from,int to)
{
    v[from].push_back(to);
}
inline void insert(int x)
{
    if(tp==1)
    {
        sta[++tp]=x;
        return;
    }
    int z=lca(sta[tp],x);
    if(z==sta[tp])
    return;
    while(dfn[sta[tp-1]]>=dfn[z]&&tp>1)
    {
        add2(sta[tp-1],sta[tp]);
        --tp;
    }
    if(z!=sta[tp])
    {
        add2(z,sta[tp]);
        sta[tp]=z;
    }
    sta[++tp]=x;
}
inline long long dfs2(int x)
{
    int sz=v[x].size();
    if(sz==0)
    return mn[x];
    long long res=0;
    for(int i=0;i<sz;++i)
    {
        int y=v[x][i];  
        res+=dfs2(y);
    }
    v[x].clear();
    return min(res,mn[x]);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n-1;++i)
    {
        int x,y;
        long long z;
        scanf("%d%d%lld",&x,&y,&z);
        add(x,y,z);
        add(y,x,z);
    }
    mn[1]=2e16;
    dep[1]=1;
    dfs(1);
    scanf("%d",&m);
    for(int i=1;i<=m;++i)
    {
        scanf("%d",&k);
        for(int j=1;j<=k;++j)
        scanf("%d",&b[j]);
        sort(b+1,b+k+1,cmp);
        sta[++tp]=1;
        for(int j=1;j<=k;++j)
        insert(b[j]);
        while(tp>0)
        {
            add2(sta[tp-1],sta[tp]);
            --tp;
        }
        printf("%lld\n",dfs2(1));
    }
    return 0;
}