动态点分治详解

动态点分治大意:利用没有树结构修改的性质预处理重心树优化时间

其实就是很暴力的思想,因为树的结构不变,所以每一次找到的重心都一样,可以用一次点分树预处理一下点分治的重心,再连接相邻的重心便是点分树。


前置知识:点分治

见另一篇blog:


算法:

好像就没什么了,上面就是全过程
详细一点的分析:

  1. 找到当前子树重心x
  2. 深搜x的每一个儿子的子树,找到每一棵子树的重心y(两个重心的话随便取一个)
  3. 对每一个y重复操作1和2
    在这里插入图片描述

这里有一棵丑陋的树,我们模拟一下建点分树的过程,"-"表示深搜和连边:

  • (1)
  • (1->3,5,8)
  • (5->2,9) (3->6,7) (8->4,13,14)
  • (2) (4) (6->10,11) (7->12) (9) (13) (14)
  • (10) (11) (12)

建出来长这样:
在这里插入图片描述
这个东西明显有两个性质:

  1. 点分树上的子树必然包括原树上的子树(请读者自行思考)
  2. 点分树的树高最大为 log ⁡ n \log n logn(证明如下,其实也应该自己思考
    重心的每一棵子树的大小都小于原树的二分之一
    那么每次至少都会把原树劈成两半
    我们最多需要 log ⁡ n \log n logn次把树劈没
    即树高最大为 log ⁡ n \log n logn,而且严重跑不满(完全二叉警告

一般来说是不需要真的建出来的,拿一个fa记一下父亲上跳就行,原因见下文。

对于大部分题都是求路径,操作为查询和单点修改,根据点分治的性质,每次修改x只会影响到点分树上根到x的点的权值,所以从下往上跳就行了。

这种问题一般不包括修改树结构的操作,因为一改全树重心都会改变,属于LCT问题,求大佬踩
对于有修改的题一般是在上一次答案的基础上修改


模板:

#include<bits/stdc++.h>
using namespace std;
int e[1000010],nxt[1000010],head[100010],cnt;
int fa[100010],dis[100010],siz[100010];
bool vis[100010]int n,q,maxn=2e9,ss,len,rt; 
void add(int x,int y){
	cnt++;
	e[cnt]=y;
	nxt[cnt]=head[x];
	head[x]=cnt;
}
//-------------------------------------------点分树 
void get_root(int x,int fa){
	siz[x]=1;
	int maxsiz=0;
	for(int i=head[x];i;i=nxt[i]){
		int y=e[i];
		if(y==fa||vis[y]) continue;
		get_root(y,x);
		siz[x]+=siz[y];
		if(siz[y]>maxsiz)
		maxsiz=siz[y];
	}
	if(ss-siz[x]>maxsiz)
	maxsiz=ss-siz[x];
	if(maxn>maxsiz){
		maxn=maxsiz;
		rt=x;
	} 
}
void get_dis(int x,int fat,int s){
	dis[++len]=s;
	for(int i=head[x];i;i=nxt[i]){
		int y=e[i];
		if(y==fat||vis[y]) continue;
		get_dis(y,x,s+1);
	}
}
void calc(int x,int s,int t){//计算答案
	len=0;
	get_dis(x,0,s);
	for(int i=1;i<=len;i++)
	//这里视题目而定
} 
void dfs(int x){
	vis[x]=1;
	for(int i=head[x];i;i=nxt[i]){
		int y=e[i]; 
		if(vis[y]) continue; 
		maxn=2e9,rt=0,ss=siz[y];
		get_root(y,-1);
		calc(y,1,2);
		fa[rt]=x;
		dfs(rt);
	}
}
//------------------------------------------主程序 
int read(){
    char c=getchar(); int x=0,f=1;
    while(!isdigit(c) && c!='-') c=getchar();
    if(c=='-') { f=-1; c=getchar(); }
    while(isdigit(c)) { x=(x<<3)+(x<<1)+c-'0'; c=getchar(); }
    return x*f;
}
int main(){
	int x,y;
	n=read();
	for(int i=1;i<n;i++){
		x=read(),y=read();
		add(x,y),add(y,x);
	}
	maxn=ss=n;
	get_root(1,0);
	dfs(rt); 
	q=read();
	for(int i=1;i<=q;i++){
	
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据结构是计算机存储、组织数据的方式,算法是解决问题的步骤和方法。数据结构和算法是计算机科学中最基础、最重要的两个领域之一。掌握数据结构和算法可以帮助我们更好地理解计算机科学的本质,提高编程能力,解决实际问题。 常见的数据结构包括数组、链表、栈、队列、树、图等。常见的算法包括排序、查找、递归、分治动态规划等。 在学习数据结构和算法时,需要掌握以下知识: 1. 时间复杂度和空间复杂度:用来衡量算法的效率和资源消耗。 2. 数组:一种线性数据结构,用来存储一组相同类型的元素。 3. 链表:一种线性数据结构,用来存储一组元素,每个元素包含一个指向下一个元素的指针。 4. 栈:一种后进先出(LIFO)的数据结构,用来存储一组元素。 5. 队列:一种先进先出(FIFO)的数据结构,用来存储一组元素。 6. 树:一种非线性数据结构,由节和边组成,每个节可以有多个子节。 7. 图:一种非线性数据结构,由节和边组成,每个节可以有多个相邻节。 8. 排序算法:用来将一组元素按照一定的顺序排列的算法,包括冒泡排序、选择排序、插入排序、快速排序、归并排序等。 9. 查找算法:用来在一组元素中查找指定元素的算法,包括线性查找、二分查找、哈希查找等。 10. 递归算法:一种通过调用自身来解决问题的算法。 11. 分治算法:一种将问题分解成多个子问题来解决的算法。 12. 动态规划算法:一种通过将问题分解成多个子问题来解决的算法,通常用于求解最优化问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值