分治就是分而治之。額……好像這話已經被說爛了。
學樹分治之前我們先來回顧下線性分治。一般的線性分治都是將大的區間分成多個小的區間(一般是兩個),然後將已處理好的小區間合併從而得到了這個大區間的結果,為了使分的次數盡可能少,應每次使分的小區間的大小盡可能平均,這樣分的次數就會是log級別的。
好,現在我們要分樹,參考線性分治的方法,每次選乙個點,使刪除這個點後被分開的連通塊大小盡可能平均,暫且把這個點稱為「中心點」。
等等我們要先了解樹分治是解決哪類問題的。個人認為:樹分治一般用來處理樹上的「全部路徑」問題。故名思意「全部路徑」就是樹上所有點對之間的路徑,看似這樣的路徑毫無規律,其實對於樹上某一點來說,我們可以的將這些路徑分為:通過這點的和永不通過這點的,其實不通過這點的路徑一定通過其他某乙個點。所以我們只需要求通過乙個點的所有路徑,求完後將這點徹底刪除,然後同樣的方法求其他點,直到遍歷完所有的點為止。(時機已成熟,看來是時候該引入點分了)還記得上面的「中心點」吧,我們如果乙個乙個點沒有一定順序的去處理雖然可以得到正確答案,但是太慢了,如果每次遞迴處理「中心點」複雜度將減少很多很多,因為這樣每次處理乙個「中心點」的時,連通塊的大小都是次數級減小的(多畫幾顆樹體會一下就知道了)。
總結一下樹上點分治的步驟:
1:找中心點
2:處理該中心點所在的連通塊中的通過該中心點的路徑的答案(比較拗口,仔細琢磨下)
3:刪除該中心點,遞迴處理與該點相鄰的連通塊
具體實現看**吧,入門題
ac**(老哥加油哦):
#include#include#include#includeusing namespace std;
const int n=10005;
struct edge
edge[n*2];
int vis[n],pre[n],siz[n],aa[n];
int sum,root,root_siz,edge_num,num,k,ans;
char ch;
inline int read()///讀入掛
inline void addedge(int a,int b,int len)///加邊
; pre[a]=edge_num;
}void dfssum(int u,int f)///求連通塊大小
}void dfsroot(int u,int f)///求中心點
mx=max(mx,sum-siz[u]);
if(mxk)r--;
if(l==r)break;
ans_num+=r-l;
l++;
}return ans_num;
}void chuli(int u)///將n個點按中心點順序依次求對答案的貢獻值
}int main()
{ int n;
while(1)
{n=read(),k=read();
if(n+k==0)break;
for(int i=1;i<=n;i++)
pre[i]=0,vis[i]=0;
int a,b,len;
edge_num=0;
for(int i=1;i
分治思想及樹上點分治
分治思想在oi中是一種常見的思想。分治的基本思想是將乙個規模為n的問題分解為k個規模較小的子問題,這些子問題相互獨立且與原問題性質相同。求出子問題的解,就可得到原問題的解。即一種分目標完成程式演算法,簡單問題可用二分法完成。當我們求解某些問題時,由於這些問題要處理的資料相當多,或求解過程相當複雜,使...
poj1741(樹上點分治)
題解 樹上分治就是一直找重心,然後計算經過重心點的答案有多少,接著再把重心這個點刪除掉然後接著在對應子樹上接著重複上面步驟直到沒有孩子結點。我是根據下面這篇部落格學習的 include include include include include include include include i...
樹上點分治學習筆記
樹上點分治,用來處理這樣一些問題,即給定一棵樹,求出和樹上所有點對之間距離相關的某個答案,如求樹上兩點之間距離等於k的點對個數 cf161d,板子題 如果了解lca,可能會想到利用lca求出所有點對間的距離,然後列舉點對統計答案,複雜度大概是o n2logn 不過看一眼題,n最大為5e5,即使時間給...