点分治&动态点分治
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)的字典序最小点对
动态点分治
区别在于递归时要记录上级重心,由此产生的父子关系构成点分树
于是在点分树上维护很多很多东西就可以支持动态修改了
大家看看这题-幻想乡战略游戏(动态维护带权重心)
题单
点分治
- [x] 洛谷 P3806 [模板]点分治1
- [x] 洛谷 P4178 Tree
- [x] 洛谷 P2634 [国家集训队]聪聪可可
- [x] 洛谷 P4149 [IOI2011]Race
- [ ] [Luogu2664]树上游戏
- [ ] [WC2010]重建计划
- [x] [HDU4812]D Tree
- [x] [SPOJ1825]Free tour II
- [ ] [HDU5977]Garden of Eden
- [ ] [HDU5909]Tree Cutting
- [ ] [[Luogu3727]曼哈顿计划E]]()
- [ ] [FJOI2014]最短路径树问题
动态点分治
- [x] [ZJOI2015]幻想乡战略游戏
- [ ] [BZOJ4372]烁烁的游戏
- [ ] [BZOJ3730]震波
- [ ] [Luogu3676]小清新数据结构题
- [ ] [LOJ6145][2017 山东三轮集训 Day7]Easy
- [x] [ZJOI2007]捉迷藏
- [x] [luogu4115]Qtree4