1. 程式人生 > 其它 >【全程NOIP計劃】樹上問題

【全程NOIP計劃】樹上問題

【全程NOIP計劃】樹上問題

最近公共祖先

問題

給定一棵樹,每次給兩個點,求他們的祖先,且該祖先為深度最小

思路

一般來說,我們想到一個暴力做法

查詢x,y的話,直接把x的祖先全部標記一遍,然後把y向上遍歷,直到遍歷到一個點,使得這個點被標記過,這個點就是x和y的最近公共祖先

或者,使得深度更大的第一個點一直向上跳,讓x和y的深度一樣,讓他們一起一步一步向上跳,直到他們遇到同一個點,這樣的方法正確性沒有問題,它的複雜度和這棵樹的深度是有關的

考慮倍增,對於剛才的做法進行優化

原來的方法:\(fa[i]\)表示i的父親

倍增的方法:\(fa[i][j]\)表示i向上跳\(2^j\)到哪個點

怎麼預處理呢?

f[i][0];
for(int j=1;j<=logn;j++)
{
    for(int i=1;i<=n;i++)
        f[i][j]=f[f[i][j-1]][j-1];
}

這樣我們就維護出來了

所以倍增lca的步驟就是:

1.先讓y的深度比x大,然後將y和x調到深度相同的位置

2.然後把log從小到大列舉,如果跳完了到的是同一個點,他們就不跳,如果不是同一個點,那麼他們就向上跳,這樣重複的話一定可以跳到同一個點

3.我們可以唯一確定該這個拆分(不考慮順序

基礎問題

題目

給一個n個點的樹,有m次查詢,每次查x到y路上的點權和

思路

可以利用樹上字首和和最近公共祖先

發現答案就是x的深度加上y的深度,減去lca的深度,再減去lca父親的深度就可以了

又一個基礎問題

題目

給一個n個節點的樹,有m次查詢,每次給定兩個點x,y,求x是不是y的祖先

思路

直接判斷x和y的最近公共祖先是不是他們其中一個不就得了

不不不,下面有一個神奇的玩意兒

DFS序

實際上就是維護一下一個點是什麼時候dfs到的,或者什麼時候走入它的子樹,走出它的子樹的

int cnt;
void dfs(int x)
{
    l[x]=++cnt;
    for(x的每個兒子)
        dfs(對應的兒子);
    r[x]=cnt;
}

對於x子樹內的每個y,都有\(l[x]\le l[y]\le r[x]\)

上面的那道題目直接看y的區間是不是在進入x的和走出x的區間之內就好了

P3128

思路

x到根的路徑加一,然後\(b[x]++\)

P2680 運輸計劃

思路

最長的最小,讓我們想到了二分答案

發現,兩條路徑只能變短,不能變長

我們可以用一個樹上差分做法來做

經典問題

題目

給一個n個節點的數,有點權,有m次查詢,每次給三個數x,y,z,求x,y之間有多少點值=z,假設值域\(O(n)\)

思路

樹上差分的做法

直接查一個點到根上面的z有多少個,dfs到一個點的時候,我們直接維護一個值域上的陣列

未公開題目

題目

給一棵n個節點的樹,有m次詢問

每次查詢:從x點開始走向y點,每秒走一步,假設在第i秒走到了i點,則答案加一,求答案

思路

P1600 天天愛跑步

思路

每個選手可以被差分掉

把x到y的路徑的選手先分成x到lca,以及lca到y

LOJ6276

思路

P1967 貨車運輸

思路

P1352 沒有上司的舞會

思路

就一個樹形DP,騷簡單

未公開題目

題目

一個圖,保證每個點最多在一個簡單環裡面,問你這個圖上面有多少個眼鏡

思路

P2607 騎士

本博文為wweiyi原創,若想轉載請聯絡作者,qq:2844938982