首页 > 其他分享 >点分治

点分治

时间:2023-10-23 17:46:12浏览次数:28  
标签:rt sz 路径 int max 分治 define

第一场 \(csp\) 后的第一篇博客,我已经 \(OIer\) 大半年了,再也不是之前那个轻狂的小朋友了。。。


所以考虑维护一种算法支持查询树上第 \(k\) 短的路径是否存在。

首先构造两个函数 \(\{dfsdist,dfssize\}\) , 当然其中 \(dfssize\) 主要是计算重心的。

这两个函数相对比较好些,其中 \(dfssize\) 函数需要一个辅助数组 \(max\),表示以 \(u\) 为根切割的最大的子树大小。还要注意作一个 \(sum\),表示递归的树整棵树的大小,然后直接用 \(rt\) 表示子树重心就好。

\(dist[i]\) 表示 \(i \rightarrow u\) 的距离。

这两个函数后面会辅助点分治的作用。

复杂度:

  • \(dfssize\) 的需要递归一遍整颗子树,复杂度也就是整颗子树的大小,可以简单地记作 \(\Theta(n)\) ,但是她并不是整个点分治复杂度瓶颈的一部分,所以这边并不需要在一这个函数的具体复杂度。

  • \(dfsdist\) 是同理的。

代码相对来说比较简单,如下。

void dfs1(int u,int f){
	sz[u]=1;
	max[u]=0;
	for(pii pr:G[u])
		if(!vis[v] && v^f){
			dfs1(v,u);
			sz[u]+=sz[v];
			max[u]=std::max(max[u],sz[v]);
		}
	max[u]=std::max(max[u],sum-sz[u]);
	if(max[u]<max[rt]) rt=u;
}

int dd[N],dist[N],tot;

void dfs2(int u,int f){
	dd[++tot] = dist[u];
	for(pii pr:G[u])
		if(v^f && !vis[v])
			dist[v]=dist[u]+w,dfs2(v,u);
	return;
}

当然,其中的 \(dfs1\) 是计算重心的,\(max[N]\) 数组是上文中的辅助数组。这里需要注意一定要计算一个 \(sum\) 的迭代,否则叶子节点一定会是最优解点,但是显然地,叶子节点不会是重心。


因为最开始的树是一颗无根树,非常不美丽,相当的不好处理。

于是我们钦定一个根 \(rt\) ,那么树上的所有路径可以分成两类:

  • 经过 \(rt\) 的路径

  • 未经过 \(rt\) 的路径

那么经过 \(rt\) 的路径又可以分为两类:

  • 以 \(rt\) 为根的路径

  • 不以 \(rt\) 为根的路径

那么我们可以把上述的三种路径描述具象一点:

1

那么考虑:

钦定节点 \(1\) 为整棵树的根节点,那么路径

\(1\rightarrow 6\) 是以 \(rt\) 为根的路径

\(5\rightarrow 7\) 是未经过 \(rt\) 的路径

\(2\rightarrow 3\) 经过 \(rt\) 的路径

那么我们考虑来维护一下这三种路径。

  • 首先是路径 \(1\) ,通过 \(dist[i]\) 数组可以直接计算出 \(K\) 长度的路径是否存在。

  • 其次是路径 \(2\) ,通过计算 \(rt\) 为根的树的子树也是可以求解的。

  • 最后是路径 \(3\) ,可以考虑对于路径 \(1\) 进行链的合并,比如说路径 \(2\rightarrow 3\) 可拆分为 \(1\rightarrow 3 + 2\rightarrow 1\)

然后直接以 \(dfsdist\) 得到的数据进行处理即可, \(dfssize\) 可以选出树的重心,然后钦定树的重心为根,再进行操作就行了。

#include <bits/stdc++.h>
#define FOR(i,s,n) for(register int i=s;i<=n;++i)
#define ROF(i,n,s) for(register int i=n;i>=s;--i)
#define ll long long
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid ((l+r)>>1)
#define lson ls,l,mid
#define rson rs,mid+1,r
#define self rt,l,r
#define pii std::pair<int,int>
#define fir first
#define sec second
#define v (pr.fir)
#define w (pr.sec)
#define gc getchar()
#define re read()
#define add push_back
const int N=1e7+10;
const int maxn=1e7+5;
std::bitset<maxn> vis;
std::vector<pii> G[N];
std::stack<int> stack;
const int inf=1e9;
int sz[N],dfn[N],max[N];
int tf[N];
int rt,sum;
int Q[N],ans[N];
int n,m;

void dfs1(int u,int f){
	sz[u]=1;
	max[u]=0;
	for(pii pr:G[u])
		if(!vis[v] && v^f){
			dfs1(v,u);
			sz[u]+=sz[v];
			max[u]=std::max(max[u],sz[v]);
		}
	max[u]=std::max(max[u],sum-sz[u]);
	if(max[u]<max[rt]) rt=u;
}

int dd[N],dist[N],tot;

void dfs2(int u,int f){
	dd[++tot] = dist[u];
	for(pii pr:G[u])
		if(v^f && !vis[v])
			dist[v]=dist[u]+w,dfs2(v,u);
	return;
}

void dfs3(int u,int f){
	tf[0]=true;
	stack.push(0);
	vis[u]=true;
	for(pii pr:G[u]){
		if(v^f && !vis[v]){
			dist[v]=w;
			dfs2(v,u);
			for(register int k=1;k<=tot;++k){
				for(register int i=1;i<=m;++i){
					if(Q[i]>=dd[k]) ans[i] |= tf[Q[i]-dd[k]];
				}
			}
			for(register int k=1;k<=tot;++k)
				if(dd[k] < maxn) stack.push(dd[k]) , tf[dd[k]]=true;
			tot=0; 
		}
	}
	while(!stack.empty())
		tf[stack.top()]=false,stack.pop();
	for(pii pr:G[u])
		if(!vis[v] && v^f){
			sum=sz[v];
			rt=0;
			max[rt]=inf;
			dfs1(v,u);
			dfs1(rt,-1);
			dfs3(rt,u);
		}
}

int read(void){
	int res=0;
	char ch=0;
	while(!isdigit(ch)) ch=gc;
	while(isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=gc;
	return res;
}

int main(){
	n=re,m=re;
	for(register int i=1;i<n;++i)
		{
			int a,b,c;
			a=re,b=re,c=re;
			G[a].add({b,c});
			G[b].add({a,c});
		}
	for(register int i=1;i<=m;++i)
		Q[i] = re;
	sum=n;
	rt=0;
	max[rt]=inf;
	dfs1(1,-1);
	dfs1(rt,-1);
	dfs3(rt,-1);
	for(register int i=1;i<=m;++i)
		if(ans[i])
			puts("AYE");
		else 
			puts("NAY");
	exit(0);
}

标签:rt,sz,路径,int,max,分治,define
From: https://www.cnblogs.com/qxblog/p/Model_Tree1.html

相关文章

  • 分治法
               ......
  • 棋盘覆盖——分治算法的典例
    问题描述在一个\({2^k}\times{2^k}(K\geqslant0)\)个方格组成的棋盘中,恰有一个方格与其他方格不同,称该方格为特殊方格。棋盘覆盖问题要求用图所示的4种不同形状的\(L\)型骨牌覆盖给定棋盘上除特殊方格以外的所有方格,且任何2个\(L\)型骨牌不得重叠覆盖。问题分析算法设计......
  • 根号分治
    前言因为觉得这个思想很有意思,最近也见到了许多使用根号分治的题目,自己也出了一些用根号分治的题目,所以想总结一下。(下文各种根号分治的名字是我掰出来的,应该有别的称呼)对文章的细节有疑问或是发现错误的欢迎提出。介绍根号分治是一种在对数据规模分类讨论的基础上利用不同算......
  • 【根号分治】P9212 「蓬莱人形」 题解
    P9212看到除法相关容易想到根号分治。先对\(x,y\)进行讨论,不妨令\(0\lex,y<m\)。\(x<y\)时,当满足\(a_i+y<m\)或\(a_i+x\gem\)时,即当\(a_i<m-y\)或\(a_i\gem-x\)满足\((a_i+x)\bmodm<(a_i+y)\bmodm\),即\(a_i\bmodm\in[0,m-y-1]\bigcup[m-x,m......
  • CDQ分治和三维偏序
    专题:CDQ分治本页面将完整介绍CDQ分治。简介CDQ分治是一种思想而不是具体的算法,与动态规划类似。目前这个思想的拓展十分广泛,依原理与写法的不同,大致分为三类:解决和点对有关的问题。1D动态规划的优化与转移。通过CDQ分治,将一些动态问题转化为静态问题。CDQ分治的......
  • cdq 分治
    cdq分治的思路仍是处理跨过当前区间中点的贡献,但递归顺序是左子区间、当前区间、右子区间。这仍然满足处理当前区间时两个子区间的相对顺序不变(但左子区间不一定是有序的)从嵌套数据结构的观点看,cdq分治就是树套树外层树的中序遍历,优点是空间少一个\(\log\)且常数更小LG4093......
  • 点分治
    点分治1.给定一个带边权的树,共有\(m\)个询问,询问距离为\(k\)的点对是否存在做法1:暴力dfs做法2:\(lca\)(时间复杂度\(O(n^2\logn)\))做法3:点分治(时间复杂度\(O(n\logn)\))思路:1.取一个节点\(u\)2.统计经过\(u\)的链经过\(u\)的链两端点必定在不同子树中记录子节......
  • 递归分治法在快速排序中的应用 java以界面的方式实现
    递归分治法在快速排序中的应用 分治法的基本思想§分治法的基本思想是将一个规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且与原问题相同。 k个子问题分别求解。如果子问题的规模仍然不够小,则再划分为k个子问题,如此递归的进行下去,直到问题规模足够小,很容易求......
  • 72ed 2023/8/25 点分治学习笔记
    起因&介绍8月22号的T3是道黑,但思路却不算太难,就去打了这是第一次接触点分治,其实之前也有过一道点分治题,叫阴阳,但当时没去改,就一拖拖了半年才学点分治类似于树形DP,但在一些地方上处理有不同就比如在跑过根结点(1),进入处理它的子树时,会将其他的一部分视作没有(emmm大概这个意思,子树......
  • 基本技巧——根号分治 学习笔记
    基本技巧——根号分治学习笔记根号分治与其说是一个算法,更不如说是一种思想(trick)。定义根号分治,是一种对数据进行点分治的分治方式,它的作用是优化暴力算法;类似于分块,但应用范围比分块更广。具体来说,对于所进行的操作,按照某个点\(B\)划分,分为大于\(B\)及小于\(B\)两个部......