在我的心目中,點分治是乙個非常難的演算法,但在解決一些樹上問題時也非常實用。為此,我特地去學了學點分治這個高深的演算法。
在學習點分治之前,我們先來解決乙個問題:什麼是樹的重心?
在一棵樹上找到乙個點,使得刪去這個點後得到的子樹中節點數最大的子樹最小,那麼這個點就叫做樹的重心。
那麼樹的重心具體有什麼作用呢?
這在之後會提到。
下面,讓我們先來看一看點分治的核心思想。
以一道模板題為例:【洛谷3806】【模板】點分治1。
第一步,我們要找到乙個節點\(rt\),將無根樹轉化為有根樹。
不難發現,在這棵樹上的路徑只有兩種:
經過根節點的路徑。
不經過根節點的路徑,則這條路徑必定位於當前根節點的某個子樹中。
對於第一種情況,我們可以在確定根節點的情況下在\(o(n)\)的時間複雜度下處理掉。
而對於第二種情況,我們就需要繼續處理這條路徑所在的子樹,並重複以上步驟,求出答案。
這個時候就不難想到分治了。
我們可以將當前處理到的子樹分成若干個小子樹,分別求出答案。
這時我們再考慮,\(rt\)取什麼節點才會使複雜度最優?
我們可以取樹的重心,使得剩下的子樹大小盡量平均,時間複雜度也會大大優化,大致為\(o(nlog^2n)\)。
#include#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define ll long long
#define swap(x,y) (x^=y,y^=x,x^=y)
#define abs(x) ((x)<0?-(x):(x))
#define fsize 100000
#define tc() (finnow==finend&&(finend=(finnow=fin)+fread(fin,1,fsize,stdin),finnow==finend)?eof:*finnow++)
#define n 10000
#define m 100
#define add(x,y,z) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].val=z)
char fin[fsize],*finnow=fin,*finend=fin;
using namespace std;
const char ans[2][4]=;
int n,m,rt,top,ee=0,lnk[n+5],size[n+5],maxsize[n+5],used[n+5],dis[n+5],stack[n+5],exist[10000005];
struct edge
e[2*n+5];
struct query
q[m+5];
inline void read(int &x)
inline void getrt(int x,int lst,int sum)//確定根節點,即找到樹的重心
if((maxsize[x]=max(maxsize[x],sum-size[x]))=x) q[i].answer|=!(exist[q[i].question-x]^t);//如果存在一條為q[i].question的路徑,就將q[i].answer更新為1
}inline void getans(int x)//處理以x為根節點的子樹
for(i=lnk[x];i;i=e[i].nxt)//列舉每乙個與當前節點相鄰的節點
if(!used[e[i].to]) getrt(e[i].to,rt=0,size[e[i].to]),getans(rt);//如果列舉到的節點沒有被作為過根節點,就對這個子樹進行處理
}int main()
初學點分治
前言 在我的心目中,點分治是乙個非常難的演算法,但在解決一些樹上問題時也非常實用。為此,我特地去學了學點分治這個高深的演算法。什麼是樹的重心 在學習點分治之前,我們先來解決乙個問題 什麼是樹的重心?在一棵樹上找到乙個點,使得刪去這個點後得到的子樹中節點數最大的子樹最小,那麼這個點就叫做樹的重心。那麼...
pyqt初學 點選按鈕開啟新介面以及退出視窗
在qt designer中設計好兩個視窗之後,在pycharm進行編譯,生成兩個py檔案。給主視窗的py檔案匯入 from 你的副視窗檔名 import 你的副視窗物件名 在retranslateui函式,你要按的按鈕下新增 self.pushbutton.clicked.connect self....
每天學點Linux
1.程序檢視刪除 ps 檢視當前正在執行的程序資訊,aux顯示所有狀態。可以確定哪些程序正在執行 執行狀態 程序是否結束 程序有沒有僵死 程序占用資源情況。eg ps aux grep tomcat kill終止程序,9強迫程序立刻終止 eg kill 9 pid killall 9 程序名 2.統...