luogu P2495

传送门:https://www.luogu.com.cn/problem/P2495

题意:给你一颗n个点的树,现在的情况是每条边有边权,现在要选取一些点,通过删除一些边的方式使得这些点与1号节点失去连通性,删除一条边的代价是边权大小。有多次询问,保证多次询问的总的点数与n同阶。

  分析:直接去跑树形dp,显然会gg。因为有多次询问,复杂度会达到惊人的(n*q)。一个直观的想法是,如果我们的复杂度只与要询问的点的数量有关的话,或许就没问题了。直接套上虚树再去跑一个树形dp即可。设dp[i]表示,将i子树中所有选中的点与该点断开的代价。显然dp[i]=dp[i]+min(dp[son[i]],weight[e])(son[i]不是被选中的点),dp[i]=dp[i]+weight[e](son[i]是被选中的点)。

#include<bits/stdc++.h>

#define all(x) x.begin(),x.end()
#define fi first
#define sd second
#define lson (nd<<1)
#define rson (nd+nd+1)
#define PB push_back
#define mid (l+r>>1)
#define MP make_pair
#define SZ(x) (int)x.size()

using namespace std;

typedef long long LL;

typedef vector<int> VI;

typedef pair<int,int> PII;

inline int read(){
    int res=0, f=1;char ch=getchar();
    while(ch<'0'|ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){res=res*10+ch-'0';ch=getchar();}
    return res*f;
}

const int MAXN = 1000005;

const int MOD = 1000000007;

void addmod(int& a, int b){a+=b;if(a>=MOD)a-=MOD;}
int mulmod(int a, int b){return 1ll*a*b%MOD;}

template<typename T>
void chmin(T& a, T b){if(a>b)a=b;}

template<typename T>
void chmax(T& a, T b){if(b>a)a=b;}

#define go(e,u) for(int e=head[u];e;e=Next[e])
int to[MAXN<<1],Next[MAXN<<1],head[MAXN],tol;
LL dis[MAXN<<1];

void add_edge(int u,int v,int val){
    Next[++tol]=head[u];to[tol]=v;head[u]=tol;dis[tol]=val;
    Next[++tol]=head[v];to[tol]=u;head[v]=tol;dis[tol]=val;
}

#define gov(e,u) for(int e=headv[u];e;e=Nextv[e])
int tov[MAXN<<1],Nextv[MAXN<<1],headv[MAXN],tolv;

void add_edgev(int u,int v){
    Nextv[++tolv]=headv[u];tov[tolv]=v;headv[u]=tolv;
}

LL minv[MAXN];

int n, h[MAXN], mark[MAXN];

int dep[MAXN],up[MAXN][25],dfn[MAXN],dfncnt;

int st[MAXN], top;

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

void dfs(int u,int f){
    dfn[u]=++dfncnt;
    for(int i=0;up[u][i];++i)up[u][i+1]=up[up[u][i]][i];

    go(e,u){
        int v=to[e];
        if(v==f)continue;
        dep[v]=dep[u]+1;up[v][0]=u;
        minv[v]=min(minv[u],dis[e]);
        dfs(v,u);
    }
}

int getLCA(int u, int v){
    if(dep[u]<dep[v]) swap(u,v);

    for(int i=20;i>=0;--i){
        if(dep[up[u][i]]>=dep[v]){
            u=up[u][i];
        }
    }

    if(u==v)return u;

    for(int i=20;i>=0;--i){
        if(up[u][i]!=up[v][i]){
            u=up[u][i];
            v=up[v][i];
        }
    }

    return up[u][0];
}

LL dfs1(int u){
    LL ret=0;LL s=0;

    gov(e,u){
        int v=tov[e];
        s+=dfs1(v);
    }

    if(mark[u])ret=minv[u];
    else ret=min(minv[u],s);

    return ret;
}

int main(){
    minv[1]=1e18;

    n=read();
    for(int i=1;i<n;++i){
        int u,v,val;
        u=read();v=read();val=read();
        add_edge(u,v,val);
    }

    dfs(1,0);

    int q=read();
    while(q--){
        int cnt=read();
        for(int i=1;i<=cnt;++i){
            h[i]=read();
            mark[h[i]]=1;
        }

        sort(h+1,h+cnt+1,cmp);
        //建立虚树
        st[top=1]=1;tolv=0;headv[1]=0;
        for(int i=1;i<=cnt;++i){
            if(h[i]!=1){
                int l=getLCA(h[i],st[top]);
                if(l!=st[top]){
                    while(dfn[l]<dfn[st[top-1]]){
                        add_edgev(st[top-1],st[top]);
                        --top;
                    }
                    if(dfn[l]>dfn[st[top-1]]){
                        headv[l]=0;add_edgev(l,st[top]);st[top]=l;
                    }else{
                        add_edgev(l,st[top--]);
                    }
                }
                headv[h[i]]=0;st[++top]=h[i];
            }
        }

        for(int i=1;i<top;++i)add_edgev(st[i],st[i+1]);
        cout<<dfs1(st[1])<<endl;
        for(int i=1;i<=cnt;++i)mark[h[i]]=0;

    }

    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/JohnRan/p/12767320.html