BZOJ2286: [Sdoi2011]消耗戰

【傳送門:BZOJ2286


簡要題意:

  給出一棵根爲1的樹,樹邊有邊權
php

  有m個詢問,每一個詢問輸入k個點的編號,求出在使得這k個點不能與根節點連通的狀況下,總共要刪掉的邊權的總和最小,求出最小總邊權html


題解:

  假如只有一個詢問,那麼咱們能夠直接dpnode

  對於樹上的點x,轉移分爲兩種狀況spa

  1.斷開本身與父親的聯繫,代價爲從根到該節點的最小值code

  2.不考慮該節點(前提是該節點不是詢問點),把子樹內的全部詢問點都斷開的代價htm

  可是若是有m個詢問的話,逐個逐個DP的複雜度顯然O(mn)會爆炸blog

  有一個地方能夠做爲突破口的就是Σk<=500000get

  就要用虛樹來作了,詳細請左轉string

  並且這題在構造的時候要注意,有兩個詢問點x,y,y在以x爲根的子樹內,就不把y建到虛樹中,由於咱們只要分割了x,y天然就被分割了it


參考代碼:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
#define Maxn 310000
using namespace std;
typedef long long LL;
struct node{int x,y,next;LL d;}a[Maxn*2];int len,last[Maxn];
void ins(int x,int y,LL d){a[++len]=(node){x,y,last[x],d};last[x]=len;}
vector<int> v[Maxn];
int dfn[Maxn],id,dep[Maxn];LL mn[Maxn];
int f[Maxn][21];
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(dep[x]>=(1<<i)&&f[x][i]!=f[y][i])
        {
            x=f[x][i];y=f[y][i];
        }
    }
    return f[x][0];
}
void dfs(int x,int fa)
{
    dfn[x]=++id;
    for(int i=1;dep[x]>=(1<<i);i++) f[x][i]=f[f[x][i-1]][i-1];
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y==f[x][0]) continue;
        mn[y]=min(mn[x],a[k].d);
        f[y][0]=x;dep[y]=dep[x]+1;
        dfs(y,x);
    }
}
int p[Maxn];
bool cmp(int x,int y){return dfn[x]<dfn[y];}
int sta[Maxn],tp;
void add(int x)
{
    if(tp==1){sta[++tp]=x;return ;}
    int lca=LCA(x,sta[tp]);
    if(lca==sta[tp]) return ;//由於阻隔了上面的點就能阻隔下面的點 
    while(tp>1&&dfn[sta[tp-1]]>=dfn[lca]) v[sta[tp-1]].push_back(sta[tp]),tp--;
    if(lca!=sta[tp]) v[lca].push_back(sta[tp]),sta[tp]=lca;
    sta[++tp]=x;
}
LL dp(int x)
{
    if(v[x].size()==0) return mn[x];
    LL d=0;
    for(int i=0;i<v[x].size();i++)
    {
        int y=v[x][i];
        d+=dp(y);
    }
    v[x].clear();
    return min(d,mn[x]);
}
int main()
{
    int n;
    scanf("%d",&n);
    len=0;memset(last,0,sizeof(last));
    for(int i=1;i<n;i++)
    {
        int x,y,d;
        scanf("%d%d%d",&x,&y,&d);
        ins(x,y,d);ins(y,x,d);
    }
    memset(mn,63,sizeof(mn));
    id=dep[1]=0;dfs(1,0);
    int m;
    scanf("%d",&m);
    while(m--)
    {
        int k;
        scanf("%d",&k);
        for(int i=1;i<=k;i++) scanf("%d",&p[i]);
        sort(p+1,p+k+1,cmp);
        tp=0;sta[++tp]=1;
        for(int i=1;i<=k;i++) add(p[i]);
        while(tp!=0) v[sta[tp-1]].push_back(sta[tp]),tp--;
        printf("%lld\n",dp(1));
    }
    return 0;
}
相關文章
相關標籤/搜索