數據結構入門9—虛樹

原本以爲K-D Tree比虛樹難,但是後來發現虛樹反而難理解一些。主要是關於找LCA的問題。

假如我們有一些點是一定要在虛樹裏的,那麼需要保證他們任意兩點的LCA也在虛樹裏,否則樹的結構就改變了。

那麼就需要用到dfs序了。

我們求得每個點的dfs序,然後把這些點按dfs序排序,那麼他們所有的LCA肯定是排序後某相鄰兩點的LCA。這個是可以證明的,我就不證了。

然後找到LCA之後就是找每個節點的虛樹父親問題了,把找到的新節點(就是找到的LCA)與原來所有節點一起按照dfs序再排一次序,因爲我們可以在初始化時找出每個點所在子樹的最大最小時間戳,所以可以維護一個棧然後判斷當前節點是否爲棧內節點的原圖中的子孫,若不是就彈棧,否則就是我們要找的父親。

模板題鏈接

//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<set>
using namespace std;
const long long maxn=250000+10,INF=2e9;
long long n,m,mi[20];
 
long long dp[maxn],len[maxn],node[maxn],zz[maxn];
int vis[maxn],fath[maxn];
 
 
long long aa;char cc;
long long read() {
    aa=0;cc=getchar();
    while(cc<'0'||cc>'9') cc=getchar();
    while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
    return aa;
}
 
int fir[maxn],nxt[2*maxn],to[2*maxn];long long v[2*maxn],e=0;
void add(int x,int y,int z) {
    to[++e]=y;nxt[e]=fir[x];fir[x]=e;v[e]=z;
    to[++e]=x;nxt[e]=fir[y];fir[y]=e;v[e]=z;
}
 
int fa[maxn][20],id[maxn],end[maxn],dep[maxn],tot,maxd;
void dfs(int pos,int d,long long minnum) {
    dep[pos]=d;id[pos]=++tot;len[pos]=minnum;
    maxd=max(maxd,d);
    for(int y=fir[pos];y;y=nxt[y]) {
        if(to[y]==fa[pos][0]) continue;
        fa[to[y]][0]=pos;
        dfs(to[y],d+1,min(v[y],minnum));
    }
    end[pos]=tot;
}
 
bool cmp(const int x,const int y) {
    return id[x]<id[y];
}
 
int getlca(int x,int y) {
    if(dep[x]!=dep[y]) {
        if(dep[x]<dep[y]) swap(x,y);
        int cha=dep[x]-dep[y];
        for(int i=18;i>=0;--i) if(mi[i]<=cha){
            cha-=mi[i]; x=fa[x][i];
            if(!cha) break;
        }
    }
    int xx,yy,z;
    while(x!=y) {
        xx=x; yy=y; z=0;
        while(xx!=yy) {
            x=xx; y=yy;
            xx=fa[xx][z];yy=fa[yy][z];
            z++;
        }
        if(z==1) { x=xx; y=yy; }
    }
    return x;
}
 
int main() {
    n=read();
    int x,y,z,xx;
    for(int i=1;i<n;++i) {
        x=read();y=read();z=read();
        add(x,y,z);
    }
    dfs(1,1,INF);
    mi[0]=1;for(int i=1;i<=18;++i) mi[i]=mi[i-1]*2;
    for(int i=1;mi[i]<=maxd+1;++i) for(int j=1;j<=n;++j) fa[j][i]=fa[fa[j][i-1]][i-1];
    m=read();
    for(int qaq=1;qaq<=m;++qaq) {
        x=read();
        for(int i=1;i<=x;++i) node[i]=read(),vis[node[i]]=1;
        sort(node+1,node+x+1,cmp);
        xx=x;//
        for(int i=2;i<=xx;++i) {
            z=getlca(node[i-1],node[i]);
            if(!vis[z]) node[++x]=z,vis[z]=2;
        }
        if(!vis[1]) node[++x]=1,vis[1]=2;
        sort(node+1,node+x+1,cmp);
        y=1;zz[1]=node[1];dp[node[1]]=0;
        for(int i=2;i<=x;++i) {
            dp[node[i]]=len[node[i]];
            while(y&&id[node[i]]>end[zz[y]]) y--;
            fath[node[i]]=zz[y];dp[zz[y]]=0;
            zz[++y]=node[i];//
        }
        for(int i=x;i>1;i--) {
            if(vis[node[i]]==2) dp[node[i]]=min(dp[node[i]],len[node[i]]);//
            else dp[node[i]]=len[node[i]];
            dp[fath[node[i]]]+=dp[node[i]];
        }
        printf("%lld\n",dp[node[1]]);
        for(int i=1;i<=x;++i) vis[node[i]]=0;
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章