2023年7月訓練 陸

2022-06-04 03:42:07 字數 2178 閱讀 8739

模板:luogo p3379 【模板】最近公共祖先(lca)

今天講的時候有點跑神,現在卑微地來補習(菜)

lca指的是最近公共祖先(least common ancestors)。

最簡單的演算法無疑是從兩個點乙個個往上走,出現的第乙個兩個點都走過的點即為兩點的lca。

但是時間很長。

所以起用倍增,倍增的作用就是將兩點上公升所需的複雜度減低

大致流程為:將deep不同的兩個點跳到同一層,再跳到deep[lca-1]的那層,再向上跳一層就是lca了。

加速跳的方法就是每次向上跳的層數為2的i次方層,就是把層數轉化成2進製的數,這樣時間複雜度就變為log2(n)了。

之後要兩個陣列f[i][j](從i向上2^j層後到達的點)和deep[i](這棵樹中i點的深度)。

deep[i]用乙個dfs求得。

f[i][j]用了遞推,f[i][j]=f[f[i][j-1]][j-1]。初始化f[i][0]也可以在遍歷整棵樹的時候求得。

然後,兩點再同時向上逼近。i從最高位開始列舉,假設兩點分別為x,y,那麼能向上跳的判斷式為:

if (f[x][i]!=f[y][i])

就是如果兩點向上跳了2^i層以後不到同乙個點就接著往上跳。為什麼這樣?因為如果往上跳了2^i層,即使到了同乙個點,它不一定是兩點的lca。

這樣做,最終就會到達lca的下面一層。隨後,我們再將兩點向上跳一層。lca求得。

然後這個讓我無比摸不著頭腦的問題出現了:

我們假設從a,b點開始,往上跳2^j層,跳到同一點。不跳。往上跳2^(j-1)層,不跳到同一點,往上跳,分別到了a',b'。顯然,這種情況是一定會存在的。那麼,從a',b』再往上跳到原來那個決定不跳的點,顯然要跳2^(j-1)層。那麼,那個點有可能是lca,也有可能不是,對吧?所以,從a',b'往上跳到lca所需的層數,是≤2^(j-1)的。換句話來說,a',b'到x的層數變成了乙個j-2位的二進位制數(可能會有前導零,也就是還可能會跳到點數相同的地方)。而此時,剛好列舉到j-2位。那麼,前導零不減,再這麼減下去,你發現,這個層數差最終會變成0,而你最終也會到達第x層。

大概就是你的叔伯(爸爸的兄弟)不是你的祖先,這裡找的祖先必須是直系的。

首先我們證明,前導零不會被減去。假設與x層的層數差為x',而你正準備往上跳y層。由於lca的層數是x+1,而lca往上的點它都不會跳,對吧?(反而,如果lca往下的點,也就是層數<=x,也就是y=x'+1,那麼就絕對不會往上跳。

顯然,當x'的該位為0,且屬於前導零,那麼只需證明x'+1<=y。而這個非常易證(假設y為10000,而x'滿足條件的最大值為01111)。所以保證,前導零是不會減去的。

接著我們證明,一旦列舉到了x'的第乙個為1的位數,這個1絕對會被減去。按照同樣的方法,假設y為10000,而x'滿足條件的最小值為10000,所以y

兩點合在一起,前導零不會減去,列舉到乙個1位就減去,最終這個層數差就會變成0.證畢。

#include#include

#include

#include

#include

#define maxn 500010#include

using

namespace

std;

int f[maxn][21

],head[maxn],deep[maxn],cnt,n,m,rt;

struct

edge

e[maxn

<<1

];void add(int u,int

v)void dfs(int x,int

fa)}

void

init()}}

}int query(int x,int

y) }

return f[x][0];}

intmain()

dfs(rt,0);

init();

while(m--)

return0;

}

2019-07-3122:54:32

2023年7月訓練(壹)

2019 07 25 luogu p3627 apio2009 搶掠計畫 卡了三個小時,看了題解才作出來的 菜 前驅知識 壹 鄰接表儲存 遍歷 貳 spfa跑最長路 改 就行了 叄 tarjan縮點 壹.鄰接表儲存 兩個,add存無邊權,未縮點 build有邊權,已縮點。void add int u...

2023年7月24日訓練日記

早上去了討論了一下昨天的b題,其實莫隊也挺好理解的,那道題直接套板子就能過,然後昨天那個二十幾行的 也理解了,思路特別巧妙,和我之前的思路差不多,只不過我少考慮了乙個點。然後上午看了一下字首和,字首和在應用中有不少技巧,不單單是對資料求和,還可以和平均數求差後求和。然後就是幾種排序,選擇排序,插入排...

2023年7月29日訓練日記

上午把昨天下午比賽的題補完了,這幾道題真是一言難盡,不難但是誰能想到啊,說明還是見的少。知識面廣的輕鬆ak。然後上午看了佇列,學了以前沒學過的雙端佇列,雙端佇列和vector差不多,就是有兩個介面,vector只能從隊尾新增,deque可以從兩端新增,功能也多了點。下午刷了幾道佇列的題,有一道以前s...