点分治超详细分析+视频讲解+题目大全(不再为点分治而烦恼!
一.点分治原理分析及应用










二.点分治视频讲解
视频链接
https://www.bilibili.com/video/BV1PE41197md?p=1
三.点分治练习题
1.P3806 【模板】点分治1
1.P3806 【模板】点分治1
分析:两点的路径分为经过根节点和不经过根节点两种,那么我们可以点分治算法,对于每一个点都作为根处理一次,计算以该节点为根的子树的所有结点到根节点的距离,然后再用一个数组标记一下,我们就可以算出是否存在点对距离为k了
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int Maxn = 1e4+10;
const int Maxk = 1e8+10;
struct Edge{
int v;
int next;
int w;
}edge[Maxn*2];
int head[Maxn];
int cnt;
void build(int u,int v,int w){
edge[++cnt].v = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt;
edge[++cnt].v = u;
edge[cnt].w = w;
edge[cnt].next = head[v];
head[v] = cnt;
return ;
}
//rt记录重心,sum记录当前树的大小,tot是计数器
int n,m,rt,sum,tot;
int siz[Maxn];
int dep[Maxn];
int tmp[Maxn];//存储距离
int maxp[Maxn];//maxp储存树的重心
int Q[Maxn];//储存询问
//judge记录距离是否存在,ans询问的答案,vis标记点是否被删除
bool judge[Maxk],ans[105],vis[Maxn];
int dis[Maxn];
//找重心,有点类似与树链剖分找重儿子
void getrt(int u,int f){
siz[u] = 1,maxp[u] = 0;//maxp初始化为0,maxp用于找重心
//遍历u的所有儿子,找出重儿子的大小maxp
for(int i=head[u];i!=-1;i=edge[i].next){
int v = edge[i].v;
//如果v是u的父节点或者v节点已经被删除,就continue
if(v==f||vis[v]) continue;
getrt(v,u);
siz[u]+=siz[v];
//更新重儿子
if(siz[v]>maxp[u]){
maxp[u] = siz[v];
}
}
//再用重儿子大小和u的父先节点比较
maxp[u] = max(maxp[u],sum-maxp[u]);
//更新重心
if(maxp[u]<maxp[rt]){
rt = u;//maxp[rt]也更新了
}
return ;
}
void getdis(int u,int f){
/*储存u为根的树的所有子节点到u的路径长度*/
tmp[++tot] = dis[u];//首先v-u存进来
for(int i=head[u];i!=-1;i=edge[i].next){
int v = edge[i].v;
if(v==f||vis[v]) continue;
dis[v]=dis[u]+edge[i].w;
getdis(v,u);
}
return ;
}
void solve(int u){
static queue<int>q;
for(int i=head[u];i!=-1;i=edge[i].next){
tot = 0;
int v = edge[i].v;
if(vis[v]) continue;//已经被删除了的点
dis[v] = edge[i].w;
getdis(v,u);
for(int j=1;j<=tot;j++)//遍历所有距离
for(int k=1;k<=m;k++){
//遍历所有询问
if(Q[k]>=tmp[j]) ans[k]|=judge[Q[k]-tmp[j]];
}
for(int j=1;j<=tot;j++){
q.push(tmp[j]);
judge[tmp[j]] = true;//设为true表示距离存在
}
}
/*把tmp数组距离初始化为false,防止影响下一个子树的判断*/
while(!q.empty()){
judge[q.front()] = false;
q.pop();
}
}
//分治
void divide(int u){
/*删除根(重心)节点,并且judge[0]=true
因为点到它本身距离为0*/
vis[u]=judge[0] = true;
solve(u);//计算经过根节点的路径
for(int i=head[u];i!=-1;i=edge[i].next){
int v = edge[i].v;
/*如果该节点已经被删除(包括父节点也已经被删了,因为是从上到下处理的)则跳过*/
if(vis[v]) continue;
maxp[rt=0]=sum=siz[v];//此时要把以v节点为根的子树作为研究对象
getrt(v,0);//找v的重心
getrt(rt,0);//以重心为根重新跟新siz数组
divide(rt);//对这颗子树进行点分治
}
}
void init(){
memset(vis,false,sizeof(vis));
memset(head,-1,sizeof(head));
return ;
}
int main()
{
init();
cin>>n>>m;
for(int i=1;i<n;i++){
int u,v,w;
cin>>u>>v>>w;
build(u,v,w);
}
for(int i=1;i<=m;i++) cin>>Q[i];
//刚开始没有重心所以为rt=0,并且设置为最大值
maxp[0]=sum=n;
getrt(1,0);//找重心
/*!此时siz数组存放的是以1为根时子树大小,
需要以找出以重心为根重算*/
getrt(rt,0);
divide(rt);//找好重心之后就可以开始分治了,求解答案
//cout<<"11\n";
for(int i=1;i<=m;i++){
if(ans[i]) cout<<"AYE\n";
else cout<<"NAY\n"

本文深入解析了点分治算法,通过实例视频教学,帮助理解其原理并提供P3806等经典练习题。从基本概念到实践应用,让你轻松掌握并解决相关问题。
最低0.47元/天 解锁文章
320

被折叠的 条评论
为什么被折叠?



