[持续更新] 杂谈点分治 点分树 边分治 链分治
Posted 蒟蒻的博客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[持续更新] 杂谈点分治 点分树 边分治 链分治相关的知识,希望对你有一定的参考价值。
个人主观题目难度评级
\\(\\mathttA:\\) 思维难度大 / 实现难的题目
\\(\\mathttB:\\) 有思维难度 / 实现有一定难度的题目
\\(\\mathttC:\\) 朴素的题目
\\(\\mathtt^x:\\) 自己独立解出的题目
量化评级:\\(\\mathtt0\\sim10\\)
时间有限刚写完,只有一些水题
以后还会添加一些难度较大的题目
以下默认数据范围 \\(1\\times 10^5\\) 级别
$$\\huge\\textbf0x01\\ \\large\\textbf点分治$$
对于树上的每个节点来说
树上所有的路径分为两种
经过该点和不经过该点的(被打
这就是点分治的大致思想
假设要解决树上路径问题
那我们先从树上拎一个点
统计经过该点的路径信息
然后就把该点从树上删去
根据上面给出的大致思想
剩下的路径都不经过该点
同时树会被分成若干子树
对子树进行同样步骤即可
直至把树上所有点全删掉
根据上面给出的大致思想
所有的路径都已被统计过
当然这个点不能随便选取
为了删空点所需次数最少
进而保证时间复杂度正确
删的是当前每棵树的重心
点分治<双指针> \\(\\mathtt[C|5.3]\\)
给定一棵有 \\(n\\) 个点的树,询问树上距离为 \\(k\\) 的点对是否存在。
点分治流程我们已经了解
考虑统计树根答案的方法
把该点所在的树遍历一遍
\\(d\\) 表示该点到树根的距离
\\(s\\) 表示该点所在子树编号
先把节点按照 \\(d\\) 大小排序
要的是不同子树中两个点
两点 \\(d\\) 的和恰好就等于 \\(k\\)
与此同时两点的 \\(s\\) 不相同
可以使用双指针进行查找
不能遍历已经删去的节点
点分治<桶> \\(\\mathtt[C|5.0]\\)
给定一棵有 \\(n\\) 个点的树,询问树上距离为 \\(k\\) 的点对是否存在。
考虑统计树根答案的方法
开大小为 \\(k\\)的桶统计路径
每个下标存储该点子树中
有无长度为该下标的路径
对于每一棵子树依次遍历
这些路径同桶内数据合并
再将这些路径放进桶里面
合并的路径必属不同子树
注意预先将树根放入桶内
View Code
Race \\(\\mathtt[C^x|5.2]\\)
给一棵树,每条边有权。求一条简单路径,权值和等于 \\(k\\),且边的数量最小。
和上一道题基本一模一样
桶储存的数据类型为 int
即长为下标的所有路径中
路径所含的边数的最小值
View Code
Tree \\(\\mathtt[C^x|5.6]\\)
给定一棵 \\(n\\) 个节点的树,每条边有边权,求出树上两点距离小于等于 \\(k\\) 的点对数量。
查询从查询单点变成前缀
树状数组维护桶数组即可
View Code
$$\\huge\\textbf0x02\\ \\large\\textbf点分树$$
点分树是跟点分治有关的树(被打
把点分治经过的点离线下
在点分树上一个点的儿子
是原树点分治中删去它后
该点所在的树
分裂成的所有子树的重心
点分树深度和点分治相同
取重心子树大小至少减半
深度 \\(\\log n\\) 非常平衡
支持原树无法完成的暴力
比如暴力跳父亲这种操作
Unnamed \\(\\mathtt[C|6.5]\\)
树,边带权,\\(q\\) 次询问,求距离某个点不超过 \\(k\\) 的点有多少个
点分树有一个重要的性质
任意两点的最近公共祖先
一定在原树中两点路径上
我们可以方便地分别处理
两点和它们点分树上 LCA
之间在原树的的路径信息
然后进行合并 以这道题为例
枚举查询点与答案点分树上的 LCA
然后对于每一个枚举的点
计算它点分树上的子树中
扣掉查询点所在的子树后
剩余部分的答案
用数据结构维护一下就行(绝对不是我写不动了
$$\\huge\\textbf0x03\\ \\large\\textbf边分治$$
边分治和点分治大致相同
只是分治对象从点变成边
对于菊花无法找合适的边
时间复杂度退化为 \\(O(n^2)\\)
需要 rebuild 变成二叉树
将儿子和一个虚点串起来
再将虚点间连边串成一串
个人感觉没有点分治简便
(如有特殊应用尽管D我
$$\\huge\\textbf0x04\\ \\large\\textbf链分治$$
针对树上的链进行的分治
它还有个别名叫树链剖分
博客另一篇文章中有叙述
(尽管还没发布…………
To Be Continued
#4707. 点分治
题解
问题在于两棵已经确定点分树的形态的树,合并后能形成多少种形态的点分树
考虑如果分裂 $(u,v)$ 这条边,那就相当于两个点分树黑白染色,各自找相同颜色的祖先,然后形成新的两个点分树
考虑如果连接 $(u,v)$ 的话,那应该就是 $u->rt_u$ 和 $v->rt_v$ 这两条路径可以任意顺序合并起来,所以考虑树形dp,类似背包转移即可
效率: $O(n^2)$
代码
#include <bits/stdc++.h> using namespace std; const int N=5005,P=1e9+7; int n,hd[N],V[N<<1],nx[N<<1],t,f[N][N],sz[N],c[N][N],g[N][N],F[N]; int X(int x){return x>=P?x-P:x;} void add(int u,int v){ nx[++t]=hd[u];V[hd[u]=t]=v; } void dfs(int u,int fr){ f[u][sz[u]=1]=1; for (int v,i=hd[u];i;i=nx[i]){ if ((v=V[i])==fr) continue; dfs(v,u); for (int j=1;j<=sz[u];j++) for (int i=0;i<=sz[v];i++) (F[i+j]+=1ll*f[u][j]*c[i+j-1][j-1]%P*g[v][i]%P)%=P; sz[u]+=sz[v]; for (int i=1;i<=sz[u];i++) f[u][i]=F[i],F[i]=0; } for (int i=sz[u];~i;i--) g[u][i]=(g[u][i+1]+f[u][i])%P; } int main(){ cin>>n;c[0][0]=1; for (int i=1;i<=n;i++){ c[i][0]=1; for (int j=1;j<=i;j++) c[i][j]=X(c[i-1][j]+c[i-1][j-1]); } for (int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x); dfs(1,0);cout<<g[1][0]<<endl;return 0; }
以上是关于[持续更新] 杂谈点分治 点分树 边分治 链分治的主要内容,如果未能解决你的问题,请参考以下文章