点分治超详细分析+视频讲解+题目大全(不再为点分治而烦恼)!

点分治算法详解:原理、视频教程与实战练习
本文深入解析了点分治算法,通过实例视频教学,帮助理解其原理并提供P3806等经典练习题。从基本概念到实践应用,让你轻松掌握并解决相关问题。

一.点分治原理分析及应用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二.点分治视频讲解

视频链接

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"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值