惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

G
GRAHAM CLULEY
Recorded Future
Recorded Future
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
小众软件
小众软件
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - 叶小钗
A
About on SuperTechFans
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
月光博客
月光博客
MongoDB | Blog
MongoDB | Blog
Apple Machine Learning Research
Apple Machine Learning Research
罗磊的独立博客
人人都是产品经理
人人都是产品经理
Engineering at Meta
Engineering at Meta
WordPress大学
WordPress大学
博客园 - Franky
Google DeepMind News
Google DeepMind News
Security Latest
Security Latest
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Project Zero
Project Zero
N
News and Events Feed by Topic
I
Intezer
C
Check Point Blog
V
Visual Studio Blog
T
Tenable Blog
博客园 - 司徒正美
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
PCI Perspectives
PCI Perspectives
H
Help Net Security
www.infosecurity-magazine.com
www.infosecurity-magazine.com
The Cloudflare Blog
SecWiki News
SecWiki News
Vercel News
Vercel News
爱范儿
爱范儿
Recent Announcements
Recent Announcements
Spread Privacy
Spread Privacy
T
Threatpost
Last Week in AI
Last Week in AI
V
V2EX
O
OpenAI News
M
MIT News - Artificial intelligence
博客园 - 【当耐特】
腾讯CDC
Forbes - Security
Forbes - Security
Microsoft Security Blog
Microsoft Security Blog
AI
AI
Hacker News - Newest:
Hacker News - Newest: "LLM"
F
Full Disclosure
C
CXSECURITY Database RSS Feed - CXSecurity.com
P
Palo Alto Networks Blog

Louis C Deng's Blog

CS231n Lecture Note: Generative Models CS231n Lecture Note: Self-Supervised Learning CS231n Lecture Note: Large Scale Distributed Training 自動微分 | DIY 實現自己的 PyTorch From RNNs to Transformers CS231n Lecture Note VII: Recurrent Neural Networks Uncovering Batch & Layer Normalization CS231n Lecture Note VI: CNN Architectures and Training CS231n Lecture Note V: Convolution Neural Networks Basics Demystifying Softmax Loss: A Step-by-Step Derivation for Linear Classifiers Backpropagation: A Vector Calculus Perspective CS231n Lecture Note IV: Neural Networks and Backpropagation CS231n Lecture Note III: Optimization CS231n Lecture Note II: Linear Classifiers CS231n Lecture Note I: Image Classification CSAPP Cache Lab II: Optimizing Matrix Transposition CSAPP Cache Lab I: Let's simulate a cache memory! CS188 Search Lecture Notes III CS188 Search Lecture Notes II How to Use TouchID for Sudo Commands on macOS CS188 Search Lecture Notes I RECAP2025: 留白 CSAPP Bomb Lab 解析 x64 暫存器速查表 CSAPP Data Lab 解析 矩陣的 Modified Gram Schmidt 方法 聊一聊位掩碼(Bit Mask) 整數溢位與未定義行為 快速排序 幾種劃分方法討論 等待 記夢(DeepSeek 輔助創作) 午夜飛行 橋樑 黎明 或 2012 RECAP2024: 水檻臥聽雨 太陽、潮落 RECAP2023: 泡沫 題解 P1622 釋放囚犯 題解 P5888 傳球遊戲 殘陽似火 再會 飢餓藝術家 卡夫卡 Python 中的 zip() 和 enumerate() 泡沫 “救救孩子……”——談魯迅和《狂人日記》 想念 淺灘 蟬 · 夏 微風 觀星 浮塵 復活 【摘錄 | 轉載】普魯斯特 《追憶似水年華》第一卷 《在斯萬家那邊》(一) Time - Pink Floyd - The Dark Side of the Moon 【轉載】靜夜思變調 高樓 幻夢 冰 RECAP2022: 流星雨 清夜 割點 Tarjan 演算法 P3147 USACO16OPEN 262144 P 題解 P3354 Riv 河流 題解 馬拉車演算法 夜雨 層霧 從愚人節玩笑到真的玩笑(bushi): 淺談 lsnotes I made my own Hexo theme 題解 紀念品分組 題解 導彈攔截 如何高效使用搜尋引擎 用 GitHub Actions 格式化 C/C++ 程式碼 四季的天空 洛谷 7 月月賽 Div.2 總結 用簡單的物理方法證明牛頓萊布尼茨公式 簡評榮耀手環6 海上生明月,天涯共此時。 我為什麼重新拿出了 iPod Swift 中的 SharedPreferance —— UserDefaults 凝視那一輪明月 用 GitHub Actions 部署 Hexo 部落格 遲來的日誌 - WWDC 2020 獎學金 vcpkg - 方便的 C/C++ 庫管理器 vimrc 配置指南 NextCloud - DIY NAS 解決方案 sudo shutdown -r now sudo shutdown -r now
題解 最近公共祖先 (LCA)
Louis C Deng · 2021-04-25 · via Louis C Deng's Blog

好久沒刷題了,複習一下:LCA。

題目詳情

題目很簡單,就是求多叉樹兩個點的最近公共祖先。

連結: 洛谷 P3379

LCA(Least Common Ancestors),即最近公共祖先,是指在有根樹中,找出某兩個結點u和v最近的公共祖先。 ———來自百度百科

818487-20151004150339121-181913844.png
圖中,4 和 3 的 LCA 就是 1。

解題

最簡單的方法 (暴力)

這種方法資料一大就會TLE。

原理很簡單,讓兩個數一個一個向上走,直到兩個數相遇。第一次相遇就是他們的 LCA。

很簡單,就不贅述了,直接上程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include <cmath>
#include <cstdio>
#include <iostream>
#include <vector>

using namespace std;

#define MAX 500000

vector<int> tree[MAX];
int dep[MAX];
int fas[MAX];

namespace M1 {
void dfs(int x, int fa) {
if (fa != -1) {
dep[x] = dep[fa] + 1;
}
fas[x] = fa;
for (int i = 0; i < tree[x].size(); i++) {
if (fas[tree[x].at(i)] == -2) M1::dfs(tree[x].at(i), x);
}
}


int solve(int a, int b) {
while (1) {
if (a == b) {
return a;
} else if (fas[a] == fas[b]) {
return fas[a];
} else if (fas[b] == a) {
return a;
} else if (fas[a] == b) {
return b;
}
int da = dep[a], db = dep[b];
int delta = abs(da - db);
if (da > db) {
for (int i = 0; i < delta; i++) {
a = fas[a];
da = dep[a];
}
} else if (da < db) {
for (int i = 0; i < delta; i++) {
b = fas[b];
db = dep[b];
}
} else {
a = fas[a];
da = dep[a];
}
}
return -1;
}
}

bool first = true;

int LCA(int a, int b, int r) {
if (first) {
M1::dfs(r, -1);
first = false;
}
int res;
res = M1::solve(a, b);
return res;
}

int main(int argc, char *argv[]) {
int m, n, s;
cin >> m >> n >> s;
for (int i = 0; i < MAX; i++) {
dep[i] = 0;
fas[i] = -2;
}
for (int i = 1; i < n; i++) {
int x, y;
cin >> x >> y;
tree[x].push_back(y);
tree[y].push_back(x);
}
dep[s] = 0;
fas[s] = -1;
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
int res = LCA(a, b, s);
cout << res << endl;
}
return 0;
}

注意這道題目的資料輸入,x y 表示x 結點和 y 結點之間有一條直接連線的邊(資料保證可以構成樹)。 所以需要用鄰接表的形式,表示多叉樹。

倍增法

這個演算法是對上面暴力演算法的最佳化。這個演算法的時間複雜度為O(nlogn)O(nlogn),已經可以滿足大部分的需求。

上述演算法中,一步一步跳太慢了,這裡我們事先做好標記,就可以每次 2i2^i 步向上跳,一直到相遇。

程式碼中有較為詳細的註釋:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <cmath>
#include <cstdio>
#include <iostream>
#include <vector>

using namespace std;

#define MAX 500001
#define MUL_MAX 22

vector<int> tree[MAX];
int dep[MAX];
int fas[MAX][MUL_MAX];
bool first = true;

int lg2(int x) {
return log(x) / log(2) + 1;
}

void dfs(int x, int fa) {
if (fa != -1) {
dep[x] = dep[fa] + 1;
}
fas[x][0] = fa;
for (int i = 1; (1 << i) <= dep[x]; i++) {
fas[x][i] = fas[fas[x][i - 1]][i - 1];



}
for (int i = 0; i < tree[x].size(); i++) {
if (tree[x].at(i) != fa) dfs(tree[x].at(i), x);

}
}

int solve(int a, int b) {
if (dep[b] > dep[a]) swap(a, b);
while (dep[a] > dep[b]) {
a = fas[a][lg2(dep[a] - dep[b]) - 1];
}
if (a == b) return a;
for (int i = lg2(dep[a]); i >= 0; i--) {
if (fas[a][i] != fas[b][i]) {
a = fas[a][i];
b = fas[b][i];
}
}
return fas[a][0];
}

int LCA(int a, int b, int r) {
if (first) {
dep[r] = 0;
fas[r][0] = -1;
dfs(r, -1);
first = false;
}
int res;
res = solve(a, b);
return res;
}

int main(int argc, char *argv[]) {
int m, n, s;
cin >> m >> n >> s;
for (int i = 0; i < MAX; i++) {
dep[i] = 0;
for (int j = 0; j < MUL_MAX; j++)
fas[i][j] = -2;
}
for (int i = 1; i < n; i++) {
int x, y;
cin >> x >> y;
tree[x].push_back(y);
tree[y].push_back(x);

}
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
int res = LCA(a, b, s);
cout << res << endl;
}
return 0;
}

其實還可以預處理出一個 lg 陣列,避免對數計算,大家可以自己去嘗試,會有一定時間最佳化效果。

無法理解倍增?這裡有個 經典資料

其他方法

實際上還有更快的方法求這道題的答案。倍增演算法已經可以滿足需求,就不再往下寫了。

  • Tarjan
  • ST 演算法

大家有興趣可以去嘗試一下。

後記

這裡放上兩種列出演算法的評分。

暴力

截圖2021-04-24 下午6.13.14.png

倍增

截圖2021-04-24 下午6.13.08.png