虛樹學習筆記


虛樹算法其實原理蠻簡單的就是,從一顆n個結點的原樹上在只取出必要結點成一顆新樹,這顆新樹必包含指定m個結點並保持原樹上的祖孫關系。

首先我們來解答一些問題

問:什么樣的結點是必要的呢??

答:指定的m個結點和 這m個結點中任意兩個結點的最近公共祖先。

問:為啥要包含最近公共祖先呢?

答:因為最近公共是虛樹的關節點,起到連接這m個點的作用

問:任意兩個結點的公共祖先構成集合,會不會有m*(m-1)/2個點?

答:其實這些祖先構成的集合最多就m-1個點,因為虛樹其實原理有點類似在樹上跑不壓縮路徑的並查集,你連接m個點最多也只需要m-1個關節點.

首先來看兩張圖,直觀感受虛樹的構建

1.構建必須包含2,4,7,6的虛樹

2.構建必須包含2,7,8的虛樹

 

 虛樹的構建方法可以看這篇文章https://www.cnblogs.com/chenhuan001/p/5639482.html

總體上就是先按dfs排個序,然后用棧加LCA對這m個結點實現深度優先遍歷並記錄路徑以方便建虛樹。最后要注意的是建邊一定要在出棧的時候建,因為有可能出現圖2的那種情況,兩個點之間還需再塞進去一個結點。

算法復雜度:

因為虛樹構建時需要對結點按dfs序排序,並求出排序后相鄰結點的LCA的所以算法復雜度O(mlog(n*m))

虛樹優點:

虛樹主要配合樹形DP食用,因為樹形DP的復雜是與樹的結點數成正比的,通過構建虛樹,可以把樹的結點數下降到2m-1以內,從而大大提高樹形DP的效率。

代碼實現:

 

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<algorithm>
  4 #include<queue>
  5 #include<vector>
  6 #include<stack>
  7 using namespace std;
  8 const int MAXN=100006;
  9 /**
 10 @var d[v]    結點v的深度
 11 @var f[v][i] 結點v的第2^i個祖先
 12 @var id[v]   結點的dfs序
 13 @var e[v]    結點v在原樹鄰接表
 14 @var vt[v]   結點v在虛樹上包含的鄰接表
 15 @var que     要查詢的結點的集合(虛樹必須包含的結點)
 16 @var LN      log2(最大深度)的向下取值
 17 @var st      棧,用於構建ST表
 18 @var z       dfs序的計數器
 19 注意結點標號必須從1開始,因為0拿去當空結點了。
 20 */
 21 int d[MAXN],f[MAXN][18],id[MAXN],LN=0,z,st[MAXN];
 22 vector<int>e[MAXN],vt[MAXN],que;
 23 int init(int n)
 24 {
 25     z=0;
 26     memset(f,0,sizeof(f));
 27     for(int i=1; i<=n; i++)
 28         e[i].clear();
 29     d[0]=-1;
 30     LN=0;
 31 }
 32 void dfs(int v,int deep)
 33 {
 34     d[v]=deep;
 35     st[deep]=v;
 36     id[v]=++z;
 37     if((1<<LN)<deep)
 38         LN++;
 39     int i,j;
 40     for(i=1,j=0; i<=deep; i<<=1,j++)
 41     {
 42         f[v][j]=st[deep-i];
 43     }
 44     for(i=0; i<e[v].size(); i++)
 45     {
 46         if(e[v][i]!=f[v][0])
 47         {
 48             dfs(e[v][i],deep+1);
 49         }
 50     }
 51 }
 52 int lca(int x,int y)
 53 {
 54     if(d[x]<d[y])
 55         swap(x,y);
 56     int i;
 57     for(i=LN; i>=0; i--)
 58     {
 59         if(f[x][i]&&d[f[x][i]]>=d[y])
 60             x=f[x][i];
 61     }
 62     if(x==y)
 63         return x;
 64     for(i=LN; i>=0; i--)
 65     {
 66         if(f[x][i]>0&&f[x][i]!=f[y][i])
 67         {
 68             x=f[x][i];
 69             y=f[y][i];
 70         }
 71     }
 72     return f[x][0];
 73 }
 74 int cmp(const int &x,const int &y)
 75 {
 76     return id[x]<id[y];
 77 }
 78 int build(vector<int> &que)
 79 {
 80     int i,j,k,temp;
 81     sort(que.begin(),que.end(),cmp);
 82     stack<int>st;
 83     st.push(que[0]);
 84     vt[que[0]].clear();
 85     for(i=1; i<que.size(); i++)///每次有出棧的時候開始建邊
 86     {
 87         k=lca(que[i],st.top());
 88         temp=0;
 89         while(!st.empty()&&lca(k,st.top())!=st.top())
 90         {
 91             if(temp)
 92                 vt[st.top()].push_back(temp);
 93             temp=st.top();
 94             st.pop();
 95         }
 96         if(st.empty()||st.top()!=k)
 97         {
 98             st.push(k);
 99             vt[k].clear();
100         }
101         if(temp)
102             vt[st.top()].push_back(temp);
103         st.push(que[i]);
104         vt[que[i]].clear();
105     }
106     temp=0;
107     while(!st.empty())
108     {
109         if(temp)
110             vt[st.top()].push_back(temp);
111         temp=st.top();
112         st.pop();
113     }
114     return temp;
115 }
虛樹

 習題:http://codeforces.com/problemset/problem/613/D

這題需要樹形DP基礎,而且要討論的情況有點多,所以其實有點不適合入門.....,因為很可能被樹形DP卡住


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM