【全程NOIP计划】树上问题

【全程NOIP计划】树上问题

最近公共祖先

问题

给定一棵树,每次给两个点,求他们的祖先,且该祖先为深度最小

思路

一般来说,我们想到一个暴力做法

查询x,y的话,直接把x的祖先全部标记一遍,然后把y向上遍历,直到遍历到一个点,使得这个点被标记过,这个点就是x和y的最近公共祖先

或者,使得深度更大的第一个点一直向上跳,让x和y的深度一样,让他们一起一步一步向上跳,直到他们遇到同一个点,这样的方法正确性没有问题,它的复杂度和这棵树的深度是有关的

考虑倍增,对于刚才的做法进行优化

原来的方法:\(fa[i]\)表示i的父亲

倍增的方法:\(fa[i][j]\)表示i向上跳\(2^j\)到哪个点

怎么预处理呢?

f[i][0];
for(int j=1;j<=logn;j++)
{
    for(int i=1;i<=n;i++)
        f[i][j]=f[f[i][j-1]][j-1];
}

这样我们就维护出来了

所以倍增lca的步骤就是:

1.先让y的深度比x大,然后将y和x调到深度相同的位置

2.然后把log从小到大枚举,如果跳完了到的是同一个点,他们就不跳,如果不是同一个点,那么他们就向上跳,这样重复的话一定可以跳到同一个点

3.我们可以唯一确定该这个拆分(不考虑顺序

基础问题

题目

给一个n个点的树,有m次查询,每次查x到y路上的点权和

思路

可以利用树上前缀和和最近公共祖先

发现答案就是x的深度加上y的深度,减去lca的深度,再减去lca父亲的深度就可以了

又一个基础问题

题目

给一个n个节点的树,有m次查询,每次给定两个点x,y,求x是不是y的祖先

思路

直接判断x和y的最近公共祖先是不是他们其中一个不就得了

不不不,下面有一个神奇的玩意儿

DFS序

实际上就是维护一下一个点是什么时候dfs到的,或者什么时候走入它的子树,走出它的子树的

int cnt;
void dfs(int x)
{
    l[x]=++cnt;
    for(x的每个儿子)
        dfs(对应的儿子);
    r[x]=cnt;
}

对于x子树内的每个y,都有\(l[x]\le l[y]\le r[x]\)

上面的那道题目直接看y的区间是不是在进入x的和走出x的区间之内就好了

P3128

思路

x到根的路径加一,然后\(b[x]++\)

P2680 运输计划

思路

最长的最小,让我们想到了二分答案

发现,两条路径只能变短,不能变长

我们可以用一个树上差分做法来做

经典问题

题目

给一个n个节点的数,有点权,有m次查询,每次给三个数x,y,z,求x,y之间有多少点值=z,假设值域\(O(n)\)

思路

树上差分的做法

直接查一个点到根上面的z有多少个,dfs到一个点的时候,我们直接维护一个值域上的数组

未公开题目

题目

给一棵n个节点的树,有m次询问

每次查询:从x点开始走向y点,每秒走一步,假设在第i秒走到了i点,则答案加一,求答案

思路

P1600 天天爱跑步

思路

每个选手可以被差分掉

把x到y的路径的选手先分成x到lca,以及lca到y

LOJ6276

思路

P1967 货车运输

思路

P1352 没有上司的舞会

思路

就一个树形DP,骚简单

未公开题目

题目

一个图,保证每个点最多在一个简单环里面,问你这个图上面有多少个眼镜

思路

P2607 骑士

posted @ 2021-11-19 15:19  wweiyi  阅读(32)  评论(0编辑  收藏  举报
js脚本