首页 > 其他分享 >点分治学习笔记

点分治学习笔记

时间:2024-12-17 22:54:59浏览次数:4  
标签:ch log 重心 复杂度 分治 路径 笔记 学习

定义

点分治,顾名思义,就是通过基于点的分治的一种算法。

通常用于处理树上问题,如树上所有路径统计。

我们设一次操作的时间复杂度为 \(O(1)\) ,那么一次点分治复杂度为 \(O(n \log n)\) 。

具体流程

操作->分治->操作……(不知道怎么讲)

点分治的核心主要在于高效的分治,每一次都可以将所求区间大小缩短至 \(\dfrac{1}{k}\) 。

因此 \(T(n)=k\times T(n/k)\) ,根据主定理,时间复杂度为 \(O(n \log n)\) 。

分治的关键在于从在每一次操作的树上寻找重心,易证以重心为根的每颗子树大小不超过 \(\dfrac{n}{2}\) ,从而可以有效缩小操作区间,保证正确的复杂度。(其实感性也很好理解的来着)

例题

P3806 【模板】点分治1

模板题,典中典。

两点间的路径分为两种:经过根的和不经过根的。

对于第二种我们在分治时可以统计到。

那么简单容斥一下就很清晰了:

  • 先找到整棵树的重心,用桶统计所有经过重心的路径,更新答案。
  • 分治,求出以重心为根的树的每颗子树的“小重心”,容斥一下发现不经过重心而经过小重心的路径即为在小重心所在子树中经过小重心的路径。(有点绕)
  • 以此类推,直到每个点都成为过重心后,答案更新完成。

一些注意点:

  • 桶中要随时保持 \(t_0=1\) ,因为都有不和其他路径组合的一种可能。
  • 扶苏在这个帖子中提到关于时空间复杂的的问题。若一次统计的常数为 \(k_1\) ,点分治自身的常数为 \(k2\),那么将询问在点分治内部统一更新的复杂度为 \(O((k_2+k_1)n\log n)\) ,每次分开更新的复杂度为 \(O(k_2k_1n\log n))\) , 显然后者大的多,故注意此实现细节。

Code

#include<bits/stdc++.h>
#define N 10005
#define M 10000005
using namespace std;

inline int read(){
    int x=0,w=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return x*w;
}

int root,n,m,tot,sum,cnt;
int head[N],q[N],dp[N],len[N],ans[N],cl[N],dis[N],siz[N];
int vis[N],t[M];
struct Edge{int to,next,w;}edge[N<<1];

void add(int u,int v,int w){
	edge[++cnt]=(Edge){v,head[u],w};
	head[u]=cnt;
}
void find(int x,int fa){
	siz[x]=1; dp[x]=0;
	for(int i=head[x];i;i=edge[i].next){
		int v=edge[i].to;
		if(vis[v]||v==fa) continue;
		find(v,x);
		dp[x]=max(dp[x],siz[v]);siz[x]+=siz[v];
	}
	dp[x]=max(dp[x],sum-siz[x]);
	if(dp[x]<dp[root]) root=x;
}
void getdis(int x,int fa){
	len[++tot]=dis[x];
	for(int i=head[x];i;i=edge[i].next){
		int v=edge[i].to;
		if(v==fa||vis[v]) continue;
		dis[v]=dis[x]+edge[i].w;
		getdis(v,x);
	}
}
void calm(int x){
	int w=0;
	for(int i=head[x];i;i=edge[i].next){
		int v=edge[i].to;
		if(vis[v]) continue;
		tot=0; dis[v]=edge[i].w; getdis(v,x);
		for(int j=1;j<=tot;++j)
			for(int k=1;k<=m;++k)
				if(q[k]>=len[j]) ans[k]|=t[q[k]-len[j]];
		for(int j=1;j<=tot;++j) 
			if(len[j]<=1e7)cl[++w]=len[j],t[len[j]]=1;
	}
	for(int i=1;i<=w;++i) t[cl[i]]=0;
}
void solve(int x){
	vis[x]=t[0]=1; calm(x);
	for(int i=head[x];i;i=edge[i].next){
		int v=edge[i].to;
		if(vis[v]) continue;
		root=0;dp[0]=n;sum=siz[v];
		find(v,x);solve(root);
	}
}

signed main(){

	//freopen("P3806_7.in","r",stdin);

	n=read();m=read();
	for(int i=1;i<n;++i){
		int u=read(),v=read(),w=read();
		add(u,v,w);add(v,u,w);
	}
	for(int i=1;i<=m;++i) q[i]=read();
	root=0;dp[0]=sum=n;
	find(1,0); solve(root);
	for(int i=1;i<=m;++i) 
		if(ans[i]) puts("AYE");
		else puts("NAY");
	//for(int i=1;i<=m;++i) printf("%d ",ans[i]);
	return 0;
}

标签:ch,log,重心,复杂度,分治,路径,笔记,学习
From: https://www.cnblogs.com/lxgswrz/p/18613585

相关文章

  • FHQ- Treap学习笔记
    FHQ-Treap与Treap都保证在第一关键字有序的情况下,维护第二关键字以达到平衡的目的。但是Treap用的是旋转,FHQ-Treap用的是分裂和合并。FHQ-Treap与Treap不同的地方:优美的分裂和合并。非旋。支持区间修改FHQ-Treap与Treap相同的地方:都保证在第一关键字有序的情况......
  • 树状数组学习笔记
    位运算是补码进行运算的因此可以解释负数进行位运算时的奇妙现象补码:正数的补码就是其本身负数的补码是在其原码的基础上,符号位不变,其余各位取反,最后+1.(即在反码的基础上+1)E:原码:10000001;补码:01111111.lowbit:lowbit这个函数的功能就是求某一个数的二进制表示......
  • 主席树学习笔记
    权值线段树就是指线段树的叶子节点保存的是当前值的个数。权值线段树一般支持以下三个操作:inserterase/removequery贴一个alphadalao的题解。主席树主席树,也叫做可持久化线段树,准确来说,应该叫做可持久化权值线段树,因为其中的每一颗树都是一颗权值线段树。经典例......
  • zkw线段树学习笔记
    先%一下zkw。stozkworzzkw线段树是一个改良版的线段树。其功能与传统线段树相同,也是用于维护区间信息。但是zkw线段树有很多优点:代码简短;纯天然非递归;常数小(尤其在差分区间更新时)特点:堆式储存,找父亲只需右移一位。建树和线段树一样,父节点左移一位为左儿子,再+1(或者|......
  • Linux 学习详细指南
    文章目录Linux学习详细指南1.基础知识准备计算机硬件与软件网络基础编程语言2.安装Linux发行版选择安装方式3.熟悉用户界面GUICLI4.学习基本命令文件系统命令用户与权限进程管理软件包管理5.深入学习Shell脚本编程系统管理安全性性能优化6.实践应用项目实践......
  • 奇绩创坛公开课第01课_创业走出第一步_陆奇:学习笔记
    写在前面背景:笔者已完整完成奇绩创坛官网上面的创业公开课,并且取得了结课证书。并且在2024年12月份参加了奇绩在我们学校的线下公开课。由于发现这些创业课程的质量很好,于是决定将学习笔记发布出来。一方面,对于学校里面的学生来说,这些经过萃取过的创业知识和方法论很有指......
  • Golang学习笔记_12——结构体
    Golang学习笔记_09——if条件判断Golang学习笔记_10——SwitchGolang学习笔记_11——指针文章目录结构体1.定义2.访问&&修改3.零值初始化&使用指针初始化4.匿名字段和嵌套结构体5.结构体方法和接收者源码结构体Go语言中的结构体(struct)是一种复合数据......
  • 代理 mitmproxy Python启动时,配置 block_global 参数,使用笔记(三)
    代理mitmproxyPython启动时,配置block_global参数,使用笔记(三)为什么要加block_global=false?,若不加,则只能本地拦截,而移动设备,或非本机请求时则无法被拦截将报错如下:Clientconnectionfrom192.167.6.166killedbyblock_globaloption注意:使用Python的非命令行启动,之前的......
  • gcc&linux静态库&动态库学习
    目录一、gcc1.gcc编译器流程2.gcc编译程序3.gcc常用参数4.多文件编译5.gcc和g++二、linux静态库和动态库1.静态库1.1生成静态链接库1.2静态库制作举例1.2.1准备测试程序1.2.2生成静态库1.3静态库的使用2.动态库2.1生成动态链接库2.2动态库制作2.3动态库的......
  • 学习Python第三天
    第一课:Python中的注释一、什么是注释:是指程序员在代码中对代码功能解释说明的标注行文字,可以提高代码的可读性。注释的内容将被Python解释器忽略,不被计算机执行。二、注释的分类:单行注释:以“#”作为单行注释的符号,作用范围内从“#”开始直到换行为止;多行注释:Python中并没有单......