<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      【學習筆記】進階算法——最近公共祖先 LCA

      前置知識

      • 樹:樹是一個特殊的圖,其邊通常為無向邊,并且一共只有 \(n-1\) 條邊,全圖連通,通常有一個根。如果題目并沒有明確指明某個節(jié)點為根,那么這棵樹就是一棵無根樹,這種情況下一般讓 \(1\) 為根。
      • 祖先:在樹上,如果節(jié)點 \(y\) 是節(jié)點 \(x\) 的祖先,當且僅當 \(y\) 出現(xiàn)在從 \(x\) 到根的最短路徑上。特別的,父節(jié)點是一個節(jié)點特殊的祖先節(jié)點。
      • ST:一種不支持修改,僅支持查詢操作的東西,耗費 \(O(n\log n)\) 的時間預處理,然后可以 \(O(1)\) 查詢一個區(qū)間的最大值/最小值/最大公因數(shù)等。要求查詢的東西滿足結(jié)合律,并且如出現(xiàn)重疊部分不會錯誤。

      最近公共祖先(LCA)是什么?

      最近公共祖先,即 Least Common Ancestors,簡稱 LCA,表示樹上兩個節(jié)點 \(x\)\(y\) 分別的祖先節(jié)點合集的交集中最深的節(jié)點。比如說:

      在上圖中,\(LCA(4,6)=1\)\(LCA(7,14)=3\)\(LCA(11,12)=7\)

      暴力求解 LCA

      求解 LCA,有一種最壞時間復雜度 \(O(n)\) 的求解 LCA 的方案。

      首先要進行預處理。以 \(1\) 為根節(jié)點跑一遍 DFS,算出每個節(jié)點的深度,并記錄下每個節(jié)點的父節(jié)點。

      void DFS(int u,int Fath){
          dep[u]=dep[Fath]+1,fa[u]=Fath;
          for(int v:g[u])if(v^fa)DFS(v,u);return;
      }
      

      這樣我們就 \(O(n)\) 得知了每個節(jié)點的深度 \(dep_u\) 以及每個節(jié)點的父節(jié)點 \(fa_u\)

      接下來就是求解 \(LCA(x,y)\) 了。

      首先,我們先要保證 \(x\)\(y\) 的深度相同。

      不妨讓 \(dep_x \ge dep_y\)(如果原本 \(dep_x < dep_y\) 那么 swap(x.y))。接下來我們不停讓 \(x=fa_x\),直到 \(dep_x=dep_y\)

      if(dep[x]<dep[y])swap(x,y);
      while(dep[x]>dep[y])x=fa[x];
      

      接下來就是求解 LCA 了。

      由于 \(x\)\(y\) 的深度已經(jīng)相同,所以我們讓 \(x\)\(y\) 不斷一個一個往上跳,直到 \(x=y\)

      while(x!=y)x=fa[x],y=fa[y];
      

      最后返回 \(x\) 即可。

      所以整個 LCA 函數(shù)大概是這個樣子:

      int LCA(int x,int y){
          if(dep[x]<dep[y])swap(x,y);
          while(dep[x]>dep[y])x=fa[x];
          while(x!=y)x=fa[x],y=fa[y];
          return x;
      }
      

      非常簡略,非常易懂。

      LCA 這么簡單嗎?當然不是。

      如何優(yōu)化暴力 LCA 算法

      剛才的 LCA 算法雖然求解一次時間復雜度還能接受,但如果處理 \(Q=10^5\) 這樣的操作,\(n\) 也是 \(10^5\) 級別的,那么時間復雜度就完全爆炸了,不能接受,考慮優(yōu)化。

      時間復雜度的瓶頸在于兩個 while 函數(shù)。我們是一個一個跳的,很慢。但如果我們按照 \(\log\) 的時間復雜度來算呢?如果我們每次跳 \(2^k\) 個呢?總之差值肯定是一個整數(shù),拆成二進制后頂多也就二十來位。這樣不就快多了?

      但是處理的東西就復雜些。本來只需要存儲自己的父節(jié)點即可,但現(xiàn)在要存儲自己往上跳 \(2^0,2^1,2^2,\dots,2^k\) 的祖先節(jié)點。看上去很麻煩,這東西好實現(xiàn)嗎?其實上很簡單,這個思想就類似于 ST。只需要在 DFS 過程中,每枚舉到一個節(jié)點 \(u\),就花費 \(O(\log n)\) 的時間計算存儲一下即可。

      那么一開始的預處理(DFS)部分的代碼就變成了這樣:

      void DFS(int u,int fa){
          dep[u]=dep[fa]+1,f[u][0]=fa;
          for(int i=0;f[u][i];i++)f[u][i+1]=f[f[u][i]][i];
          for(int v:g[u])if(v^fa)DFS(v,u);return;
      }
      

      其中 \(f_{i,u}\) 表示節(jié)點 \(u\) 往上跳 \(i\) 步后所處節(jié)點。如果跳爆了(樹沒有那么大)那么值為 \(0\)

      那么 LCA 求解過程中的兩個 while 函數(shù)的時間復雜度就都可以得到改善啦!從 \(O(n)\) 一下變?yōu)?\(O(\log n)\),很爽吧?

      具體來說,就是利用 \(f\) 數(shù)組來跳躍。

      細節(jié)見代碼吧。

      int LCA(int u,int v){
          if(dep[u]>dep[v])swap(u,v);
          for(int i=20;i>=0;i--)
              if(dep[u]<=dep[v]-(1<<i))v=f[v][i];
          if(u==v)return u;
          for(int i=20;i>=0;i--)
              if(f[u][i]!=f[v][i])u=f[u][i],v=f[v][i];
          return f[u][0];
      }
      

      這樣就快多了。不是嗎?

      然后你就可以把這些碎片代碼組裝起來,加上 main 函數(shù),通過模板題了。

      例題選講

      \(1\)

      樹上前綴和。簡單題。

      具體來說就是一開始 DFS 的時候加一個 \(s\) 數(shù)組,每次 \(s_u = s_{fa} + a_u\)

      然后計算 \(x\)\(y\) 的路徑時就直接 \(s_x + s_y - 2 \times s_{LCA(x,y)} + a_{LCA(x,y)}\) 即可。

      注意特判一下 \(x=y\) 的情況——直接輸出 \(a_x\) 即可。

      代碼是十萬年以前寫的,很丑。就不貼了。

      \(2\)

      樹上差分。簡單題。

      這完全是板子樹上差分啊喂!

      就是說,你還是要維護一個 \(d\) 數(shù)組,然后差分的時候需要推一推式子。

      在本題中,你只需要:

      int x,y,lca;cin>>x>>y;lca=LCA(x,y);
      d[x]++,d[y]++,d[lca]--,d[f[lca][0]]--;
      

      即可。

      然后最后跑一遍,恢復一下就行。

      void CNT(int u,int fa){
          for(int v:g[u])
              if(v^fa)CNT(v,u),d[u]+=d[v];
          ans=max(ans,d[u]);return;
      }
      

      輸出 \(ans\) 即可。

      總結(jié)

      LCA 兩大特色,一個樹上差分,一個樹上前綴和。相比于后者,前者的應用要更廣泛一些。

      很多時候 LCA 也會用來輔助各種東西。

      究其根源,LCA 就是用來計算樹上兩個節(jié)點的路徑的長度的!當然啦,LCA 也很聰明,它支持存在邊權(quán)/點權(quán)之類的附屬條件的樹哦!

      這就是 LCA,圖論中的好伙伴!

      posted @ 2025-08-02 10:01  嘎嘎喵  閱讀(13)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产男女猛烈无遮挡免费视频| 成人无码区免费视频| 大又大又粗又硬又爽少妇毛片| 中文字幕 日韩 人妻 无码| 亚洲精品一区二区毛豆| 含紧一点h边做边走动免费视频| 亚洲区成人综合一区二区| 风骚少妇久久精品在线观看| 欧美日韩免费专区在线观看 | 野花香视频在线观看免费高清版| 欧美牲交a欧美牲交aⅴ图片| 欧洲人妻丰满av无码久久不卡| 国产av无码国产av毛片| 国产精品一区二区插插插| 91密桃精品国产91久久| 成年女人片免费视频播放A| 深夜av在线免费观看| 国产激情av一区二区三区| 爽爽精品dvd蜜桃成熟时电影院| 依兰县| 国产精品一区二区在线欢| 高清偷拍一区二区三区| 在线欧美精品一区二区三区| 国产亚洲精品VA片在线播放 | 温泉县| 亚洲三级香港三级久久| 免费人成网站视频在线观看| 同仁县| 亚洲国语自产一区第二页| 国产精品免费第一区二区| 老妇肥熟凸凹丰满刺激| 欧洲亚洲国内老熟女超碰| 亚洲成色精品一二三区| 国内精品久久久久影视| 日韩乱码人妻无码中文字幕视频| 国产精品av中文字幕| 国产精品欧美福利久久| 高清无码爆乳潮喷在线观看| 亚洲岛国成人免费av| 亚洲sm另类一区二区三区| 亚洲成片在线看一区二区|