[持续更新] 杂谈点分治 点分树 边分治 链分治

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;
}

 

以上是关于[持续更新] 杂谈点分治 点分树 边分治 链分治的主要内容,如果未能解决你的问题,请参考以下文章

分治动态点分治 ([ZJOI2007]捉迷藏)

点分树点分治

点分树点分治

#4707. 点分治

[bzoj1095][ZJOI2007]Hide 捉迷藏 点分树,动态点分治

[WC2018]即时战略——动态点分治(替罪羊式点分树)