点分治学习笔记
OI中有一类在树上与路径有关的题目。
如果直接枚举两个端点复杂度至少O(n^2)通常无法承受。
如果考虑枚举路径的LCA通过一些奇技淫巧计算,通常至少需要dfs一遍子树,复杂度依旧无法承受。
因此可以考虑将所有路径分为两类:经过x的和不经过x的。
将经过x的路径全部处理完了之后,相当于是把x去掉,原图变成森林,对每一颗树进行类似做法。
显然如果每一层计算的复杂度是O(n)的话那么最终复杂度取决于层数。
显然希望x的每颗子树不会大于原来的sz的一半,其实就是在求重心。
做法是堆每个点计算其子树最大值,更新即可。
模板:(未编译不知对不对)
int get_rt(int x,int f)
{
sz[x]=1;maxsz[x]=0;
for(int i=h[x];i;i=e[i].pre)
if(e[i].to!=f&&!vis[e[i].to])
{
get_rt(e[i].to,x);
sz[x]+=sz[e[i].to];
maxsz[x]=max(maxsz[x],sz[e[i].to]);
}
maxsz[x]=max(maxsz[x],full_sz-sz[x]);
if(maxsz[x]<maxsz[rt]) rt=x;
return 0;
}
int get_sz(int x,int f)
{
sz[x]=1;
for(int i=h[x];i;i=e[i].pre)
if(!vis[e[i].to]&&e[i].to!=f)
sz[x]+=get_sz(e[i].to,x);
return sz[x];
}
int calc(int x,int f)
{
//solve it!
}
int dfs(int x)
{
rt=0;get_rt(x,0);
get_sz(x=rt,0);vis[x]=true;
for(int i=h[x];i;i=e[i].pre)
if(!vis[e[i].to]) calc(e[i].to,x);
for(int i=h[x];i;i=e[i].rpe)
if(!vis[e[i].to]) dfs(e[i].to);
}
int main()
{
full_sz=n;maxsz[rt]=n+1;
dfs(1);return 0;
}