樹分治亂學

2022-09-19 15:36:14 字數 3034 閱讀 1461

\(\mathrm\color}\) 輕鬆切點分治然後還寫了\(\huge\leftarrow}\)

orz.

神 \(\mathrm\color}\) 說 : 所謂點分治,就是把所有的點分開來治

那麼,什麼是點分治? 點分治 就是 澱粉汁 也就是為了勾芡而調配的水澱粉,一種乳白色懸濁液,那麼,一道色香味俱全的糖醋排骨就做好啦!(大霧).

通過對於整個樹形結構的遍歷,一次性解決大量對於樹鏈的詢問.

對於一棵樹,其任意兩點之間都有唯一一條簡單路徑,於是總的路徑數就是 \(\frac\) , 是 \(n^2\) 級別的,顯然直接列舉每條路徑然後維護是很劣的.

那麼,在遍歷一棵樹的時候,把樹盡可能均勻分成兩部分,那麼整體就是 \(\mathrm(n \log n)\) 了.

為了更為平均,引入樹的重心.

對於樹上的每乙個點,算其所有子樹中最大的子樹節點數,這個值最小的點就是這棵樹的重心.

而重心有乙個性質 : 以樹的重心為根時,所有子樹的大小都不超過整棵樹大小的一半.

求重心也很簡單, \(\mathrm(n)\) 的一次 dfs 即可.

那麼問題就來了,大量的求重心複雜度會很高嗎 ?

實際是不會的.只要總是找到重心,子樹就能盡量均分,這樣就可以實現找重心總體 \(\mathrm(n \log n)\)

可以發現,分治 + 求重心的過程都是 \(\mathrm(n \log n)\) 的,於是總體複雜度就是 \(\mathrm(n \log n)\).

然後分的部分解決後,考慮如何處理路徑問題.

首先,對於目前分治到的乙個點 \(u\) (這時候這個點是重心,作為根節來用),可以求出 \(u\) 到其子樹內所有的點的路徑的資訊,然後遞迴得到 \(u\) 子樹內路徑的資訊,最後回溯到 \(u\) 時,合併這些資訊和 \(u\) 父親的資訊,拼接在一起得到跨越分治到的根的資訊.

\(\texttt\)

code :

int head[n],ecnt = -1;

struct edge e[n << 1];

inline void addedge(int st,int ed,int w) ,head[st] = ecnt;

e[++ecnt] = (edge) ,head[ed] = ecnt;

}int n,m;

int q[105];

bool res[105],vis[n];

bool buc[10000005];

int dist[n],tmpd[n];

int siz[n],f[n];

int totsiz,rt,cnt;

#define max(a,b) ((a) > (b) ? (a) : (b))

void calcsiz(int u,int _f)

f[u] = max(f[u],totsiz - siz[u]);

if(f[u] < f[rt]) rt = u;

}void calcdist(int u,int _f)

}std::queuetag;

void solve(int u,int _f)

while(!tag.empty())

buc[tag.front()] = 0,tag.pop();

fe(i,u)

}int mian()

ff(i,1,m) q[i] = read();

rt = 0,f[rt] = inf;

totsiz = n;

calcsiz(1,1);

calcsiz(rt,rt);

solve(rt,rt);

ff(i,1,m) if(res[i])

putc('a'),putc('y'),putc('e'),enter;

else

putc('n'),putc('a'),putc('y'),enter;

end_io();

return 0;

}

從查詢有沒有改為了字首和,樹狀陣列即可.

但是我的點分治好慢啊.

\(\texttt\)

code :

int head[n],ecnt = -1;

struct edge e[n << 1];

inline void addedge(int st,int ed,int w) ,head[st] = ecnt;

e[++ecnt] = (edge) ,head[ed] = ecnt;

}int n;

int q,ans;

bool vis[n];

int dist[n],tmpd[n];

int siz[n],f[n];

int totsiz,rt,cnt;

#define max(a,b) ((a) > (b) ? (a) : (b))

void calcsiz(int u,int _f)

f[u] = max(f[u],totsiz - siz[u]);

if(f[u] < f[rt]) rt = u;

}void calcdist(int u,int _f)

}#define lb(x) (x & (-x))

int t[20005];

void m(int p,int val)

}int q(int p)

return ret;

}std::queuetag;

void solve(int u,int _f)

cnt = 0;

}while(!tag.empty())

fe(i,u)

}

和點分治相似,同樣是先選擇邊再均分整棵樹,但是可以發現,對於乙個二叉樹,這個過程進行的最平均,不然可能會被卡.

那麼考慮新建一些不影響統計過程的結點,然後分配一下其餘點使其成為二叉樹.

帶修好難啊.

以後再補吧.

分治亂做亂講

水平有限,題目太水 or 和分治關係不大 求輕噴 kk 考慮如何把乙個序列的每乙個區間的集合都求出來。按權值分治成大小兩個序列,遞迴下去,然後再合併到一起。需要的操作次數是 t n 2t n 2 看起來不錯但是過不去。再對值域分塊,每個塊分別做,詢問的時候把這幾個塊合併到一起。設塊大小為 b 則操作...

分治入門 樹分治

分治思想 劃分子問題,解決子問題,合併子問題 題目 poj1741 題意 給定一棵含有n個節點的無向帶權樹,滿足距離 k的兩點共有多少對?n 1e4 題解 1 首先找到樹a的重心,重心指的是乙個節點,將該節點刪去之後剩下的最大子樹的節點數最小 將其作為樹a的根。在數列的分治之中我們是直接去區間的中間...

點分治(樹分治)

將原問題分解成若干相同形式,相互獨立的子問題,各個擊破 一般用來解決有關樹上路徑的統計和詢問 p4178 tree 給定一棵 n 個節點的樹,每條邊有邊權,求出樹上兩點距離小於等於 k 的點對數量。暴力做法 o n2 點分治做法 選擇乙個點作為分治中心,令其為rt做dfs。對於一條路徑path u,...