首页 > 其他分享 >点分治

点分治

时间:2024-10-11 09:02:55浏览次数:9  
标签:head 子树 int siz 分治 dp dis

感觉非常有深度,感觉过几天就又要忘了,所以我写个题解。

P3806 【模板】点分治 1

给定一棵有 \(n\) 个点的树,询问树上距离为 \(k\) 的点对是否存在。

题意非常简单 题意越短越毒瘤

大佬原文

我们先想想点对有几种情况:

第一种是经过根节点的路径;

第二种是不经过根节点的路径;

想第一种有路径距离 \(dis(u,v)=dis(u,root)+dis(v,root)\)。

而第二种呢,只是到了他们的另一个公共祖先,即另一个根,发现是不是有点分治的感觉了,大子树分为小子树,依次处理点对距离。

image

image

但是不做任何优化的话可能会有一个很深的子树,看大佬的图图

这时候依次处理就很慢了,此时我们就要找到重心(删掉重心后剩下的子树尽可能地平衡(分出来的最大子树的size尽可能地小))

然后就没了,反复找重心,处理距离就可以了,然后我就讲下代码如何实现吧。

找重心,找树中的每个点,找到一个节点最大子树所拥有的节点数尽可能小。

void getroot(int x,int f){
	siz[x]=1;//树大小
	dp[x]=0;//最大的子树
	for(int i=head[x];i;i=e[i].next){
		int y=e[i].v;
		if(vis[y]||y==f){//不能是父节点
			continue;
		} 
		getroot(y,x);//遍历子节点
		siz[x]+=siz[y];//更新大小
		dp[x]=max(dp[x],siz[y]);//最大子树的大小 
	}
	dp[x]=max(dp[x],sum-siz[x]);//父节点的子树大小也算 
	if(dp[x]<dp[root]){//最大的子树更小,更新重心为根
		root=x;	
	}
}

我们找到了个根,想知道到所以点的距离怎么办,计算!!!

void getdis(int x,int f){
	rev[++tot]=dis[x];//有这么一个距离(记住了)
	if(dis[x]>1e7){//洛谷机制路径太大不合法
		return
	}
	for(int i=head[x];i;i=e[i].next){
		int y=e[i].v;
		if(vis[y]||y==f){
			continue;
		}
		dis[y]=dis[x]+e[i].w;//更新距离
		getdis(y,x);//更新距离
	}
}

此时知道根到每个点的距离,那该处理答案了吧。

void doit(int x){
	int c=0;//记录有多少个距离
	for(int i=head[x];i;i=e[i].next){
		int y=e[i].v;
		if(vis[y]){
			continue;
		}
		tot=0;//有几个路径
		dis[y]=e[i].w;//更新距离
		getdis(y,x);
		for(int j=1;j<=tot;j++){
			for(int k=1;k<=m;k++){//遍历询问
				if(query[k]>=rev[j]){
					pax[k]|=pd[query[k]-rev[j]];//如果有这么个值更新
				}
			} 
		}
		for(int j=1;j<=tot;j++){
			pd[rev[j]]=1;//表示有这么个值
			q[++c]=rev[j];//为了清除
		}
	}
	for(int i=1;i<=c;i++){
		pd[q[i]]=0;//清除,优化时间
	}
}

此时该向下递归根节点了。

void solve(int x){
	vis[x]=1,pd[0]=1;
	doit(x);//处理当x为根,所有点对的距离 
	for(int i=head[x];i;i=e[i].next){
		int y=e[i].v;
		if(vis[y]){
			continue;
		}
		sum=siz[y];//设子树大小 
		root=0;//更新子树重心 
		getroot(y,x);//找新子树的重心
		solve(root);
	}
}

好了上面就是主要的代码了,你应该蒙了吧,确实会晕的,但菜就多练。

点击查看完整代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e7+10;
#define int ll
int n,m;

struct ss{
	int v,w,next;
}e[N];
int cnt=2,head[N];
void add(int u,int v,int w){
	e[cnt].v=v;
	e[cnt].w=w;
	e[cnt].next=head[u];
	head[u]=cnt++;
} 


int query[N];

int vis[N],siz[N],root,dp[N],sum,pd[N];

int rev[N],q[N],dis[N],pax[N];

void getroot(int x,int f){
	siz[x]=1;
	dp[x]=0;
	for(int i=head[x];i;i=e[i].next){
		int y=e[i].v;
		if(vis[y]||y==f){
			continue;
		} 
		getroot(y,x);
		siz[x]+=siz[y];
		dp[x]=max(dp[x],siz[y]);//最大子树的大小 
	}
	dp[x]=max(dp[x],sum-siz[x]);//父节点的子树大小也算 
	if(dp[x]<dp[root]){//找子树最小 
		root=x;	
	}
}
int tot=0;
void getdis(int x,int f){
	rev[++tot]=dis[x];//记录距离 
	if(dis[x]>1e7){
		return
	}
	for(int i=head[x];i;i=e[i].next){
		int y=e[i].v;
		if(vis[y]||y==f){
			continue;
		}
		dis[y]=dis[x]+e[i].w;//更新距离 
		getdis(y,x);
	}	
}

void doit(int x){
	int c=0;
	for(int i=head[x];i;i=e[i].next){
		int y=e[i].v;
		if(vis[y]){
			continue;
		}
		tot=0;
		dis[y]=e[i].w;
		getdis(y,x);
		for(int j=1;j<=tot;j++){
			for(int k=1;k<=m;k++){
				if(query[k]>=rev[j]){
					pax[k]|=pd[query[k]-rev[j]];
				}
			} 
		}
		for(int j=1;j<=tot;j++){
			pd[rev[j]]=1;
			q[++c]=rev[j];
		}
	}
	for(int i=1;i<=c;i++){
		pd[q[i]]=0;	
	}
} 

void solve(int x){
	vis[x]=1,pd[0]=1;
	doit(x);//处理当x为根,所有点对的距离 
	for(int i=head[x];i;i=e[i].next){
		int y=e[i].v;
		if(vis[y]){
			continue;
		}
		//dp[0]=n; 
		sum=siz[y];//设子树大小 
		root=0;//更新子树重心 
		getroot(y,x);
		solve(root);
	}
}

signed main(){
    ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cin>>n>>m;
	
	for(int i=1;i<=n-1;i++){
	getroot(1,0);
	solve(root);
	
	for(int i=1;i<=m;i++){
		if(pax[i]){
			cout<<"AYE\n";
		} 
		else{
			cout<<"NAY\n";
		} 
	}
    return 0;
}

标签:head,子树,int,siz,分治,dp,dis
From: https://www.cnblogs.com/sadlin/p/18457685

相关文章

  • 分治法
    算法导论这个文档是学习“算法设计与分析”课程时做的笔记,文档中包含的内容包括课堂上的一些比较重要的知识、例题以及课后作业的题解。主要的参考资料是Introductiontoalgorithms-3rd(ThomasH.)(对应的中文版《算法导论第三版》),除了这本书,还有的参考资料就是Algorithmsdesi......
  • 【模板】"动态DP"&动态树分治
    目录题目描述朴素算法矩阵刻画实现code以洛谷模板题为例介绍动态dp的一般方法。P4719【模板】"动态DP"&动态树分治-洛谷|计算机科学教育新生态(luogu.com.cn)P4751【模板】"动态DP"&动态树分治(加强版)-洛谷|计算机科学教育新生态(luogu.com.cn)题目描述给定一......
  • CDQ分治
    前置知识 归并排序(一维偏序) 推荐写法:https://www.cnblogs.com/didiao233/p/17986130第1题   归并排序 查看测评数据信息给定你一个长度为n的整数数列。请你使用归并排序对这个数列按照从小到大进行排序。并将排好序的数列按顺序输出。输入格式 输入共......
  • 洛谷题单指南-分治与倍增-P6648 [CCC2019] Triangle: The Data Structure
    原题链接:https://www.luogu.com.cn/problem/P6648题意解读:在一个n行的数字三角形中,求所有边长为k的正三角形最大值之和。解题思路:1、枚举法枚举每一个边长为k的三角形,在其中求max,然后累加,n最多3000,时间复杂度是n^4,显然超时。2、倍增和ST思想此题非常类似于RMQ问题,也就是求区......
  • [2023四校联考3]sakuya 题解(根号分治)
    题目链接。题目分析第一个操作类似哈希冲突那一道题,可以运用类似的思路开一个二维表,很容易想到两种做法:开一个二维表,表上的第\(i\)行,第\(j\)列表示序列下标在模\(i\)意义下等于\(j\)的加法标记。对于修改操作,直接暴力修改对应的那一行的值即可,查询时用线段树查询那个......
  • 99th 2024/9/4 CDQ分治小结
    概括轻新小思路用于处理点对关系题在能将点对分段处理(如下)的题目中有奇效试想,现在要处理部分点对,其范围为\(l\in[L,R],r\in[L,R]\)其位置不确定,但是我们可以考虑将其分为三个板块分别作出的总贡献即分为\(l\in[L,mid],r\in[mid+1,R]\)\(l\in[L,mid],r\in[L,mid]\)\(l......
  • CDQ分治学习笔记
    CDQ分治学习笔记k维偏序问题求满足条件的二元组个数题意描述每个元素有\(k\)个值,要求满足(以\(k=2\)为例)\(a_j\lea_i,b_j\leb_i\)的点对个数。分析这实际上就是我们熟悉的逆序对问题,回忆一下我们是怎么处理的,首先来说,当\(a,b\)其中一个的含义是下标的时候可以直......
  • 树分治
    点分治点分治适合处理大规模的树上路径信息问题。本质思想是选择一点作为分治中心,将原问题划分为几个相同的子树上的问题,进行递归解决。常见的用于统计树上有关路径的信息。假设当前选定根节点为\(rt\),则符合条件的路径必然是以下两种之一:经过\(rt\)或一段在\(rt\)上;在......
  • 揭秘合并排序:分治排序初学者指南
    归并排序由约翰·冯·诺依曼于1945年提出,主要是为了提高大型数据集的排序效率。冯·诺依曼的算法旨在使用分而治之的方法提供一致且可预测的排序过程。这种策略允许归并排序有效地处理小型和大型数据集,保证在所有情况下都能实现稳定的排序,时间复杂度为o(nlogn)。合并排序采用......
  • 洛谷题单指南-分治与倍增-P4155 [SCOI2015] 国旗计划
    原题链接:https://www.luogu.com.cn/problem/P4155题意解读:在m个点的环上,有n个区间,且各个区间之间没有包含关系,计算从每个区间开始,最少要多少个区间能覆盖环上所有m个点。解题思路:本质上是一个区间覆盖问题!1、破环成链由于题目中是一个环,对于环的问题,在区间DP中介绍过,经典处理......