1. 程式人生 > 其它 >樹上演算法01-倍增LCA/Trie

樹上演算法01-倍增LCA/Trie

樹-相關演算法

定義

  • 任意兩個節點之間只有唯一一條路徑的無向圖

  • \(n\)個節點,\(n - 1\)條邊

建樹方法

鏈式前向星(提供邊的資訊)

//儲存
struct edge{
    int to;
    int pre;
}e[ll];
//加邊
void add(int x, int y){
    e[++cnt].to = y;
    e[cnt].pre = last[x];
    last[x] = cnt;
}
//遍歷
for(int i = last[cur];i;i = e[i].pre){
       if(e[i].to != fa)dfs(e[i].to,cur);
   }

二叉/三叉連結串列

大家都會寫

LCA演算法

  • 對於兩個節點,首先其深度不同
  • 移動一個節點直到二者深度相同
  • 深度相同以後再向上尋找祖先
  • 從距離最遠的祖先開始跳,如果找到的祖先不相同就跳
  • 最後停在LCA下面一層

倍增法

查詢

int lca(int x,int y){
    if(depth[x] > depth[y]){
        int tmp;
        tmp = x;
        x = y;
        y = tmp;
    }//y is always deeper
    int two = 0;
    int d = depth[y] - depth[x];
    while(d){
        if(d%2)y = f[y][two];
        ++two;
        d /= 2;
    }
    if(x == y) return y;
    //一起跳
    for(int i = 20; i >= 0; --i){
        if(f[x][i] != f[y][i]){
            x = f[x][i];
            y = f[y][i];
        }
    }
    return f[x][0];
}

是誰把多叉樹當二叉樹做啊

是我啊

那沒事了

DFS預處理

  • DFS預處理,求出每個節點的不同級別祖先
  • 記錄深度
void dfs(int cur,int fa){
    depth[cur] = depth[fa] + 1;
    f[cur][0] = fa;
    for(int i = 1; (1<<i) < depth[cur]; ++i){
        f[cur][i] = f[f[cur][i-1]][i - 1];
    }
   for(int i = last[cur];i;i = e[i].pre){
       if(e[i].to != fa)dfs(e[i].to,cur);
   }
}

差分陣列

給出一個數組{\(a_i\)}

差分陣列為{\(a_i - a_{i -1}\) }\((a_0 = 0)\)

便於對某一段區間進行統一的+n的操作,僅需改變區間頭尾兩個數字

查詢原陣列中某個數即求一段字首和

差分樹

\(def:fa = fa - ls - rs\)

支援對u到v路徑上的所有點權值修改後,只需修改u、v、pla、pla ‘ s father四個點的值即可

所有修改結束以後將樹還原

進行後續查詢操作

樹上預處理-邊

dfs將每條邊上的值放到下面的節點裡

異或題:每個節點儲存到根的異或值

樹上最短路

詢問路徑長度

差分樹:\(fa - (ls +rs)\)

DFS預處理:每個點記錄到根的長度

\(Length_{uv} = l_u + l_v - 2l_{LCA}\)

包含點集最小子樹的帶權路徑和

三個點其實與兩個點的沒有太大差別,實在是弱搞得好複雜

對於任意三個點

可以用這個方法:\((1\rightarrow2+1\rightarrow3+2\rightarrow3)/2\)

然後來看四個點

如果把所有點兩兩跑一遍

\(length = a + b+a+c+a+d+b+c+b+d+c+d\)

\(length = 3(a+b+c+d)\)

再來看這棵樹

\(length = a+b+a+b+c+a+d+c+b+d+c+b+d\)

??不符合條件

需要用到虛樹相關知識(pks)

trie樹(儲存字串用於多模式串匹配)

儲存實現

int node[lll][30];//記錄節點兒子的編號

功能函式

  • insert
void insert(string s){
    int root = 0;
    for(int i = s.length() - 1; i >= 0; --i){
        int d = s[i] - 'a';
        if(!node[root][d])node[root][d] = ++cnt;
        root = node[root][d];
    }
e[root] = true;
}
  • find
bool find(string s){
    int root = 0;
    for(int i = 0; i < s.length(); ++i){
        int d = s[i] - 'a';
        if(!node[root][d])return false;
        root = node[root][d];
    }
    if(e[root])return true;//結束位置
    return false;
}