[关闭]
@Holmesee 2020-10-28T09:01:53.000000Z 字数 3846 阅读 600

树论

讲稿

by


  1. 题目描述一律放在引用里,简单的例题可能无标题无且solution也放在引用里。例子:

    (题目描述)这是一道简单的例题
    sol:!@#¥%%……

  2. 题目太简单不要D我,太难也不要D我。
  3. 稿子写得比较简单,大家要认真听。
  4. 推荐一个图论作图工具 Graph Editor
  5. 推荐一个编辑器 Typora
  6. 推荐一个数学作图工具 GeoGebra

1. 轻工业

1.1 XX序

  1. dfs序:仅在第一次访问 时加入
  2. 括号序: 在第一次访问 与结束访问 时加入
  3. 欧拉序: 在第一次访问 与每次回溯 时都加入

明显dfs序可以对应子树。

给定一棵n个节点的树,m次查询,每次查询需要求出某个节点深度为h的所有子节点。

1.2 维护权值和

维护权值和这个问题,有这么几种常见情况:
1. 单点修改子树询问
2. 子树修改单点询问
3. 子树修改子树询问
4. 链修改单点询问
5. 单点修改链询问
请大家自行思考怎么做

1.3 重链剖分

给定一棵带边权的树,要求支持两种操作:
1.修改某条边的权值
2.询问树中两点之间唯一路径上的最大边权。
sol:线段树维护最大值,完了。

当然,也有稍微复杂点的。

[SDOI2011]染色

给定一棵 个节点的无根树,共有 个操作,操作分为两种:

  1. 将节点 到节点 的路径上的所有点(包括 )都染成颜色
  2. 询问节点 到节点 的路径上的颜色段数量。

颜色段的定义是极长的连续相同颜色被认为是一段。例如 112221 由三段组成:112221

先解决序列问题,线段树维护颜色段数、左端颜色、右端颜色,合并随便讨论下。然后轻重链切换时注意也要合并。

2 树的直径与重心

2.1 树的直径

结论1: 离任意一个点最远的点一定是直径的一端。
这就导出第一种求法,两遍bfs。
第二种求法是dp,维护子树内最长链与次长链。
结论2: 两棵树合并后,新树的直径端点一定从原树直径端点中产生。

很多树论题一眼不会,可以从直径入手思考。

[APIO2010]巡逻

有一棵树,我们可以在树上加上 条边,使从一号节点出发遍历每条边并最终回到一号节点经过的路径最短。



  1. 加入一条边会形成一个环,环上所有边都少走一次。直接连直径。

  2. 可以扩展 的结论,再找一个环。但是一条边最多少走一次,因此把第一次求出的直径边权赋为 再求一次直径即可。注意这次不能两遍bfs。

2.2 树的重心

为奇数只有一个重心, 为偶数可能两个重心。

甩一波结论:

  1. 删除重心后所得的所有子树,节点数不超过原树的1/2;
  2. 树中所有节点到重心的距离之和最小,如果有两个重心,那么他们距离之和相等;
  3. 两个树通过一条边合并,新的重心在原树两个重心的路径上;
  4. 树删除或添加一个叶子节点,重心最多只移动一条边。

[CSP2019]树的重心

给出一棵树,我们知道,断掉一条边后,它会分成两棵子树,定义这条边的贡献为这两棵子树的重心的编号和。求所有边的贡献和。

考虑每个点的贡献。
先用整棵树的重心作根。
除了 以外点 ,断掉 子树内的点显然不能使 成为重心。
的子树大小, 的重儿子的大小, 为断掉的边, 另一棵(不包括 那棵)子树的大小。
BKmmwV.png
如图, 号点为 ,假设要断掉
那么要使 成为圆圈里面这棵子树的重心,要满足

  1. 的子树外。

先不管第一条,第二条和第三条合并为
这个大致做法是用树状数组动态维护每个满足条件2.3.的 的个数,询问就是区间查。
然后减掉在 子树内的合法边,这个可以用类似天天爱跑步的做法,考虑进 与出 的变化值。

还要算出 的贡献。
的儿子中子树最大的节点为 ,次大的节点为
中,则需满足
中,则需满足
可以顺便处理。

3 基环树

  1. @ 已经讲过了。
  2. 我会的是 @ 讲过的的子集。
  3. 所以我不用讲了。

4 小清新例题

4.1 [HDU5156]

节点树,根为 ,一个节点可能有多个不同颜色的苹果,但苹果总数为 。任务是:对于每个节点,输出它的子树内有多少种颜色的苹果。

思路1

对每种颜色分开考虑贡献。对每个颜色搞个虚树,要做到虚树上每个点只贡献一次。

思路2

转线性问题,假设要询问 中的颜色种数,求出 表示上一个与 颜色相同的位置,那么


扩展到树上相当于询问 个子树区间。这是个经典的二维数点问题,扫描线解决。

BQ3vmF.png

4.2 [NOIP2016]天天爱跑步

个节点的树,有 个玩家,第 个玩家会在时刻 出现在 然后以每秒一条边的速度沿唯一路径跑到 ,然后瞬间消失。每个节点上有一个观察员,他会在 时刻记录在 号节点停留的玩家个数 。求出 数组。

玩家们都可以拆成从下到上的路径,比如一种情况下, 有贡献当且仅当 开个全局桶统计。具体地说,看进 的子树,与出 的子树时桶的差值。注意 lca 处可能会算重,要减去。

4.3 [CF280C] Game on Tree

给定一棵有根树,每次随机选一个未被删除的点,将以它为根的子树删除。
求删除整棵树所用的期望步数。

期望的线性性。
你可以理解成,总的期望等于每个点贡献期望的和。
一个点有贡献,即被作为根删除的概率是多少呢?

所以说遇到期望题优先考虑一下期望的线性性,就像遇到位运算题优先按位考虑。

4.4 ???

给你一棵树,问距离为 的倍数的无序点对有多少个。

如果淀粉质学傻了可能直接上点分了,但这题不需要。
表示以 为根的子树中到 距离 mod 3等于 的点的个数。
合并点 的儿子时,顺便统计当前儿子与前缀儿子组成的贡献。

4.5 [JZOJ5050]颜色树

个点的树,每个点有一种颜色。一共有 种颜色。求含 种颜色的路径数量。

Ps:出题人特意卡了空间 禁用状压 DP

状压咋做?。FWT优化到 。这咋过???
[JZOJ5050]颜色树 咋状压?
在座的能教教我吗。

正解大概是 的容斥做法。枚举不选颜色集合 ,删掉这些点,然后剩下一坨联通块,求出两端在不同联通块的路径数量。

4.6 [HDU4912]

个节点的树,给出 条路径 ,你需要选出尽量多的路径,要求路径之间没有公共点。

贪心,考虑从下往上贪心地选取
自底向上,对于lca在 上的一条路径,如果可选,那么一定会选。
怎么判断是否可选?
若选取了lca为 的一条路径,将整棵子树 均标记,注意若发现一个点已标记即可返回。

4.7 [bzoj1117]救火站

给你一棵 节点的树,现在要建立一些消防站,有以下要求:
1.消防站要建立在节点上,每个节点可能建立不只一个消防站。
2. 每个节点应该被一个消防站管理,这个消防站不一定建立在该节点上。
3. 每个消防站可以管理至多 个节点。
4. 消防站只能管理距离(两点间最短路径的边数)不超过 的结点。请问至少要设立多少个消防站。

我们的策略是,到万不得已时才放消防站。
表示 的第 层还有多少节点需要管理, 表示 的第 层还余下多少控制力。

  1. void dfs(int x,int f)
  2. {
  3. fa[x]=f; need[x][0]=1;
  4. for(int i=head[x];i;i=e[i].next)
  5. {
  6. int to=e[i].to;
  7. if(to==fa[x]) continue;
  8. dfs(to,x);
  9. for(int j=1;j<=k;j++) need[x][j]+=need[to][j-1];
  10. for(int j=k-1;j>=0;j--) lef[x][j]+=lef[to][j+1];
  11. }
  12. ll tmp=(need[x][k]+s-1)/s;
  13. ans+=tmp;
  14. lef[x][k]+=(ll)tmp*s;
  15. for(int i=0;i<=k;i++)
  16. {
  17. if(lef[x][i])
  18. {
  19. for(int j=i;j>=0&&(j>=i-1||x==1);j--)
  20. {
  21. if(lef[x][i]<=need[x][j])
  22. {
  23. need[x][j]-=lef[x][i];
  24. lef[x][i]=0;
  25. break;
  26. }
  27. lef[x][i]-=need[x][j];
  28. need[x][j]=0;
  29. }
  30. }
  31. }
  32. }
  33. int main(){
  34. ……
  35. dfs(1,0);
  36. for(int i=0;i<=k;i++) tot+=need[1][i];
  37. ans+=(tot+s-1)/s;
  38. ……
  39. }

4.8 [CF735E]Ostap and Tree

问对一棵树染色,初始无色,要求距每个点最近的染色点的距离不超过 的染色方法总和

题解

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注