点分治
点分治&动态点分治
Tags:数据结构
作业部落
评论地址
点分治
一、板子
鉴于没有人会看我的博客学习点分治,所以就不详细解说算法了,提供一个板子逼着自己背下来
板子:[luogu3806]【模板】点分治1
求树上是否存在距离为\(K\)的路径,多组询问
点分治支持以下函数
Getroot
Getdis
Calc
solve
1.找重心
void Getroot(int x,int fa)
{
siz[x]=1;mx[x]=0;
for(int i=head[x];~i;i=a[i].next)
{
int R=a[i].to;
if(R==fa||vis[R]) continue;
Getroot(R,x);
siz[x]+=siz[R];
mx[x]=max(mx[x],siz[R]);
}
mx[x]=max(mx[x],sum-siz[x]);
if(mx[x]<mx[root]) root=x;
}
2.把树上距离存进临时数组
void Getdis(int x,int fa)
{
dis[++cnt]=dep[x];
for(int i=head[x];~i;i=a[i].next)
{
int R=a[i].to;
if(vis[R]||R==fa) continue;
dep[R]=dep[x]+a[i].w;
Getdis(R,x);
}
}
3.点分治不同题的区别就在于Calc函数
void Calc(int x,int d,int op)
{
cnt=0;
dep[x]=d;
Getdis(x,0);
sort(dis+1,dis+cnt+1);
for(int i=1;i<=M;i++)
{
int l=1,r=cnt;
while(l<r)
if(dis[l]+dis[r]>K[i]) r--;
else
{
if(dis[l]+dis[r]==K[i])
for(int k=r;dis[k]==dis[r]&&k>=l;k--)
Yes[i]+=op;
l++;
}
}
}
4.递归计算,分层找重心
void Solve(int x)
{
Calc(x,0,1);
vis[x]=1;int tt=sum;
for(int i=head[x];~i;i=a[i].next)
{
int R=a[i].to;
if(vis[R]) continue;
Calc(R,a[i].w,-1);
sum=siz[R]>siz[x]?tt-siz[x]:siz[R];//siz要注意了————Cwen
root=0;
Getroot(R,x);Solve(root);
}
}
5.\(main\)函数中的调用
int main()
{
root=0;mx[0]=sum=N;
Getroot(1,0);Solve(root);
}
注意有些题不能容斥
二、用处
在我所做过的题中,他可以用来求解树上路径问题
1.长为\(k\)(小于\(k\))的路径条数(以及这条路径的最少边数)
2.长为三的倍数的路径条数
3.路径点权之积\(\%p=k\)的字典序最小点对
动态点分治
区别在于递归时要记录上级重心,由此产生的父子关系构成点分树
于是在点分树上维护很多很多东西就可以支持动态修改了
大家看看这题-幻想乡战略游戏(动态维护带权重心)