分治思想在oi中是一種常見的思想。分治的基本思想是將乙個規模為n的問題分解為k個規模較小的子問題,這些子問題相互獨立且與原問題性質相同。求出子問題的解,就可得到原問題的解。即一種分目標完成程式演算法,簡單問題可用二分法完成。
當我們求解某些問題時,由於這些問題要處理的資料相當多,或求解過程相當複雜,使得直接求解法在時間上相當長,或者根本無法直接求出。對於這類問題,我們往往先把它分解成幾個子問題,找到求出這幾個子問題的解法後,再找到合適的方法,把它們組合成求整個問題的解法。如果這些子問題還較大,難以解決,可以再把它們分成幾個更小的子問題,以此類推,直至可以直接求出解為止。這就是分治策略的基本思想。
總結來說,分治就是「分而治之」,可以把乙個複雜的問題簡單化,從全域性變成區域性,逐漸縮小問題的規模,更加高效的解決問題。
在我們計算機入門的過程中,其實分治思想已經接觸得不少了。最基礎又經典的例子便是快速排序,歸併排序和快速冪。
不妨先來複習一下這三種「基本功」:
快速排序:
void qsort(int l,intr) qsort(l,j-1
); qsort(j,r);
}
歸併排序:
void msort(int l,intr)
while(ib[k++]=a[i++];
while(jb[k++]=a[j++];
memcpy(a+l,b+l,(r-l)*sizeof(int
));}
快速冪(非遞迴):
int pow(int x,inty)}
快速冪(遞迴):
int pow(int x,inty)
return
ans ;
}
將這幾段**互相比較,便可以較為容易地加深對分治思想的理解。以後在一些高階的演算法中也會經常用到分治思想。
例如:矩陣乘法快速冪加速遞推
cdq分治
整體二分
快速傅利葉變換
快速沃爾什變換
莫比烏斯變換
辛普森積分等等。
以上的一些問題大多數都屬於序列分治的範疇內。了解了分治思想之後,將其與樹上問題結合起來,便是我們的樹上分治了。由於樹上的點分治在樹的分治問題中操作相對簡單,用途廣泛,本文主要深入研究樹上的點分治問題。
首先,樹上點分治是用來幹什麼的呢?通常用來處理樹上路徑統計問題。先選取乙個部分,統計經過這個部分的全部路徑數,再遞迴地處理該部分的每乙個子樹,易知,「部分」的選取在樹的點分治問題中十分重要。一般我們採取的方式是,對於乙個節點,處理出其左子樹資訊,再處理出右子樹資訊,然後將兩部分的資訊結合起來,放在一起統計,從而達到分治處理樹的統計問題的目的。那麼我們上文也已經提到,「部分」,即分治中心不能隨意選取,一般我們選取重心,原因與時間複雜度有關,論證較為複雜,在此不多贅述。重心的求法在以前樹上問題的學習中應該已經明確,這裡給出**:
void getroot(int x,intfa) }
mx[x]=max(mx[x],sn-si[x]);
if(mx[root]>mx[x])
root=x;
}
求出重心之後,利用樹中所有點到重心的距離,統計滿足條件的路徑,然後遞迴子樹,繼續進行分治過程。在統計的過程中,一般情況下我們可以通過單步容斥原理,即用「從子樹中任意選出兩個點」的結果減去「從同乙個兒子的子樹中任意選出兩個點」的結果,得到每次分治的結果。對於 x 的每乙個兒子,用同樣的方法便可得到正確統計結果。注意,在用單步容斥原理時需要滿足乙個條件,即統計結果可以逆運算,如果不存在逆運算,最典型的例子便是統計最大值和最小值,像這樣的問題只能就題論題,通過特殊方法加以解決。
兩道模板題:poj 1741 bzoj 2152
最裸的點分治練習題,初學者可以嘗試一下。其中bzoj 2152題解戳
樹上點分治入門
分治就是分而治之。額 好像這話已經被說爛了。學樹分治之前我們先來回顧下線性分治。一般的線性分治都是將大的區間分成多個小的區間 一般是兩個 然後將已處理好的小區間合併從而得到了這個大區間的結果,為了使分的次數盡可能少,應每次使分的小區間的大小盡可能平均,這樣分的次數就會是log級別的。好,現在我們要分...
poj1741(樹上點分治)
題解 樹上分治就是一直找重心,然後計算經過重心點的答案有多少,接著再把重心這個點刪除掉然後接著在對應子樹上接著重複上面步驟直到沒有孩子結點。我是根據下面這篇部落格學習的 include include include include include include include include i...
樹上點分治學習筆記
樹上點分治,用來處理這樣一些問題,即給定一棵樹,求出和樹上所有點對之間距離相關的某個答案,如求樹上兩點之間距離等於k的點對個數 cf161d,板子題 如果了解lca,可能會想到利用lca求出所有點對間的距離,然後列舉點對統計答案,複雜度大概是o n2logn 不過看一眼題,n最大為5e5,即使時間給...