\(\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,...