1. 程式人生 > >[模板] 虛樹 && bzoj2286-[Sdoi2011]消耗戰

[模板] 虛樹 && bzoj2286-[Sdoi2011]消耗戰

所有 += gist for swa inf namespace bzoj clu

簡介

虛樹可以解決一些關於樹上一部分節點的問題. 對於一棵樹 \(T\) 的一個子集 \(S\), 可以在 \(O(|S| \log |S|)\) 的時間復雜度內求出 \(S\) 的虛樹.

虛樹包括根節點, 所有詢問點和所有詢問點之間的 \(lca\).

代碼

//store the tree
struct tg{
    struct te{int t,pr,v;}edge[nsz*2];
    int hd[nsz],pe=1;
    void adde(int f,int t,int v){edge[++pe]=(te){t,hd[f],v};hd[f]=pe;}
    void adddb(int f,int t,int v){adde(f,t,v);adde(t,f,v);}
#define forg(g,p,i,v) for(int i=g.hd[p],v=g.edge[i].t;i;i=g.edge[i].pr,v=g.edge[i].t)
    void clear(){//only g2
        pe=1;
//      rep(i,1,pu)hd[usedp[i]]=0;
    }
}g1,g2;

//get lca (with sparse table)
namespace nlca{
    void dfs(int u,int fa){
        eul[++pe]=u,vis[u]=pe,d[u]=d[fa]+1;
        forg(g1,u,i,v){
            if(v==fa)continue;
            dfs(v,u);
            eul[++pe]=u;
        }
    }
    int dmin(int a,int b){return d[a]<=d[b]?a:b;}
    void rmq(){
        rep(i,1,pe)stt[i][0]=eul[i];
        rep(j,1,l2n[pe]){
            rep(i,1,pe+1-(1<<j)){
                stt[i][j]=dmin(stt[i][j-1],stt[i+(1<<(j-1))][j-1]);
            }
        }
    }
    int stqu(int a,int b){
        int l=l2n[b-a+1];
        return dmin(stt[a][l],stt[b-(1<<l)+1][l]);
    }
    void eulinit(){
        int l=0;
        rep(i,1,n*3){
            if(i==(1<<(l+1)))++l;
            l2n[i]=l;
        }
        dfs(1,0);
        rmq();
    }
    int lca(int a,int b){
        int x=vis[a],y=vis[b];
        if(x>y)swap(x,y);
        return stqu(x,y);
    }
}

// 求虛樹
// line[1...k]: 用到的點
bool cmp(int a,int b){return vis[a]<vis[b];}
int stk[nsz],top=0;
void build(){
    g2.clear(),top=0;
    sort(line+1,line+k+1,cmp);
    top=0,stk[++top]=1;
    rep(i,1,k){
        int l=nlca::lca(line[i],stk[top]);
        while(top>1&&vis[stk[top-1]]>=vis[l])g2.adde(stk[top-1],stk[top],1),--top;
        if(l!=stk[top])g2.adde(l,stk[top],1),stk[top]=l;
        stk[++top]=line[i];
    }
    while(top>1)g2.adde(stk[top-1],stk[top],1),--top;
}

//dfs 過程
void sol(int p){
    forg(g2,p,i,v){
        sol(v);
        //do something...
    }
    g2.hd[p]=0; //清空虛樹
}

例題: BZOJ2286 [Sdoi2011]消耗戰

建立虛樹之後dp即可.

註意輸入的節點必須斷掉, 但lca節點可斷可不斷. 可以標記輸入的節點.

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
using namespace std;
#define rep(i,l,r) for(register int i=(l);i<=(r);++i)
#define repdo(i,l,r) for(register int i=(l);i>=(r);--i)
#define il inline
typedef double db;
typedef long long ll;

//---------------------------------------
const int nsz=250050;
const ll ninf=1e18;

int n,m,k,line[nsz];
int used[nsz];

struct tg{
    struct te{int t,pr,v;}edge[nsz*2];
    int hd[nsz],pe=1;
    void adde(int f,int t,int v){edge[++pe]=(te){t,hd[f],v};hd[f]=pe;}
    void adddb(int f,int t,int v){adde(f,t,v);adde(t,f,v);}
#define forg(g,p,i,v) for(int i=g.hd[p],v=g.edge[i].t;i;i=g.edge[i].pr,v=g.edge[i].t)
    void clear(){//only g2
        pe=1;
//      rep(i,1,pu)hd[usedp[i]]=0;
    }
}g1,g2;

ll mind[nsz]{0,ninf};

int l2n[nsz*3+50];
int eul[nsz*3],pe=0,vis[nsz],d[nsz];
int stt[nsz*3][21];

namespace nlca{
    void dfs(int u,int fa){
        eul[++pe]=u,vis[u]=pe,d[u]=d[fa]+1;
        forg(g1,u,i,v){
            if(v==fa)continue;
            mind[v]=min(mind[u],(ll)g1.edge[i].v);
            dfs(v,u);
            eul[++pe]=u;
        }
    }
    int dmin(int a,int b){return d[a]<=d[b]?a:b;}
    void rmq(){
        rep(i,1,pe)stt[i][0]=eul[i];
        rep(j,1,l2n[pe]){
            rep(i,1,pe+1-(1<<j)){
                stt[i][j]=dmin(stt[i][j-1],stt[i+(1<<(j-1))][j-1]);
            }
        }
    }
    int stqu(int a,int b){
        int l=l2n[b-a+1];
        return dmin(stt[a][l],stt[b-(1<<l)+1][l]);
    }
    void eulinit(){
        int l=0;
        rep(i,1,n*3){
            if(i==(1<<(l+1)))++l;
            l2n[i]=l;
        }
        dfs(1,0);
        rmq();
    }
    int lca(int a,int b){
        int x=vis[a],y=vis[b];
        if(x>y)swap(x,y);
        return stqu(x,y);
    }
}

bool cmp(int a,int b){return vis[a]<vis[b];}
int stk[nsz],top=0;
void build(){
    g2.clear(),top=0;
    sort(line+1,line+k+1,cmp);
    top=0,stk[++top]=1;
    rep(i,1,k){
        int l=nlca::lca(line[i],stk[top]);
        while(top>1&&vis[stk[top-1]]>=vis[l])g2.adde(stk[top-1],stk[top],1),--top;
        if(l!=stk[top])g2.adde(l,stk[top],1),stk[top]=l;
        stk[++top]=line[i];
    }
    while(top>1)g2.adde(stk[top-1],stk[top],1),--top;
}

ll dp[nsz];
void sol(int p){
    dp[p]=0;
    forg(g2,p,i,v){
        sol(v);
        dp[p]+=(used[v]?mind[v]:min(mind[v],dp[v]));
    }
    g2.hd[p]=0;
}

int main(){
    ios::sync_with_stdio(0),cin.tie(0);
    cin>>n;
    int a,b,c;
    rep(i,2,n){
        cin>>a>>b>>c;
        g1.adddb(a,b,c);
    }
    nlca::eulinit();
    cin>>m;
    rep(i,1,m){
        cin>>k;
        rep(j,1,k)cin>>line[j],used[line[j]]=1;
        pe=1;
        build();
        sol(1);
        cout<<dp[1]<<'\n';
        rep(j,1,k)used[line[j]]=0;
    }
    return 0;
}

[模板] 虛樹 && bzoj2286-[Sdoi2011]消耗戰