Tree POJ 1741 (樹分治入門)

2021-08-28 14:38:58 字數 1879 閱讀 8578

參考**:

這題的兩個關鍵部分: 1. 求出樹的重心(這在之前的部落格裡面提到,不在敘述)

2. 如何進行分治。

首先,如果我們選定乙個點作為根節點,那麼一條路徑要麼經過這個根節點,要麼不經過這個根節點。

題目要求出所有路徑小於等於k的路徑,我們可以這樣考慮(先不考慮重複的),依次把每個點都當做根節點,那麼我們只要dfs一遍就可以求出所有點到根節點的距離,然後考慮兩兩組合,只要符合,我們就統計。

上面的思路必然會有重複的,第一,在同一子樹上的兩個點,都連向根的話,必然重複了一段路徑,所以這部分應該刪除。

所以我們的思路就是先直接求出以乙個點為根節點的所有滿足個數,然後遞迴到子樹,減去在子樹中滿足的(在子樹中滿足,對應於前面的,就是在同一顆子樹重複的)。

對於以下這棵樹:

顯然a點是它的重心。

我們假設現在分治到了a點(當前點為a)

我們一開始求解貢獻時,會有以下路徑被處理出來:

a—>a

a—>b

a—>b—>c

a—>b—>d

a—>e

a—>e—>f (按照先序遍歷順序羅列)

那麼我們在合併答案是會將上述6條路徑兩兩進行合併。

這是注意到:

合併a—>b—>c 和 a—>b—>d 肯定是不合法的!!

因為這並不是一條樹上(簡單)路徑,出現了重邊,我們要想辦法把這種情況處理掉。

處理方法很簡單,減去每個子樹的單獨貢獻。

例如對於以b為根的子樹,就會減去:

b—>b

b—>c

b—>d

這三條路徑組合的貢獻

接著就是**了(應該看**更直觀一點)

#include#include#include#include#include#include#include#include#include#include#includeusing namespace std;

const int maxn=1e4+7;

const int inf=0x3f3f3f3f;

typedef long long ll;

const int mod=1e9+7;

int n,k,allnode;

int head[maxn*2];

int num;

int dp[maxn];

int size[maxn];

int focus,m;

ll dist[maxn];

int deep[maxn];

bool vis[maxn];

ll ans;

struct edge

edge[maxn<<2];

void addedge(int u,int v,int w)

void init()

void getfocus(int u,int pre)

dp[u]=max(dp[u],allnode-size[u]);

if(m>dp[u])

}void dfs(int u,int pre)

}int cal(int x,int now)

else r--;

}return ans;

}void solve(int x)

}int main()

focus=ans=0;

allnode=n,m=1e9;

getfocus(1,0);

solve(focus);

printf("%lld\n",ans);

}return 0;

}

Tree POJ 1741 點分治 尺取

想到點分治倒是不難,但是怎麼總是tle呢?這個就很煩了,然後就想到了一種把solve 函式裡的那個 尺取的主要方式 while l r else r include include include include include include include include include inc...

POJ 1741 樹的分治

題意就是求樹上距離小於等於k的點對有多少個 n2的演算法肯定不行,因為1w個點 這就需要分治。可以看09年漆子超的 本題用到的是關於點的分治。乙個重要的問題是,為了防止退化,所以每次都要找到樹的重心然後分治下去,所謂重心,就是刪掉此結點後,剩下的結點最多的樹結點個數最小。每次分治,我們首先算出重心,...

POJ 1741 樹分治入門

include include include include using namespace std const int inf 0x3f3f3f3f const int max 1e4 5 點分治 cursize 樹當前大小 curroot 當前樹的根 son i 節點i的子節點的個數 d i ...