1.树上拓扑排序计数
结论$\dfrac{n!}{\prod\limits_{i=1}^n size_i}$
对于节点$i$,其子树随意排序的结果是$size[i]!$
但$i$需要排在第一位,只有$size[i]-1$个数可以任意排
乘上$\frac{1}{size[i]}$
2.DAG上的问题退化成有向树解决
如果转化为DAG问题的题目,如果边与边之间有传递关系
可以退化成树进行解决
在建树的时候需要关心的是某一个点的直接父亲是什么
如ATcoder的ABC158F
3.在基环树上DP
主要思想是将基环树上的环断开,然后做树形DP
最后处理这一条边对答案的影响
4.在数列上划分并要求划分区域递增的DP
基本的DP转移是
$dp[i][j]=max{dp[j][k]}+v(i)$
其中 $i$到$j$的区间和大于 $j$到$k$的区间和
朴素复杂度$O(n^3)$
可以使用单调队列优化到$O(n^2)$
利用固定j的取值
所以$i$增加的同时,$k$的范围也相应变大,利用单调队列和单指针维护
例如P3089
但如果$v(i)$有单调性,可优化到$O(n)$,如P5665
5.求$depth[LCA(i,j)]$处理办法
将$i$到$root$的路径所有的点权值加一,然后询问$j$到$root$的路径的点权之和,就是$LCA$的深度
$LCA$可以看成两个点到根的两条链的第一个交点
6.LCT维护边权
将边看作一个点,将这个代表边的点的点权赋为这条边的边权,并将其他的点边权设为$0$或其他不影响答案的值
在连边或断边时,设这条边两端的端点为$u$,$v$,代表边的点编号为$id$
link(u,id),link(id,v);
cut(u,id),cut(id,v);
7.LCT维护子树信息
维护一个点由虚边连结的点的信息$si$,然后再维护所有的边(包括虚边和$Splay$上左右儿子)相连的儿子和自己的信息$s$
因为虚实边会随着LCT的操作不断变化
首先在$pushup$的时候,$s[x]=s[son[x][0]]+s[son[x][1]]+si[x]+val[x]$,保证$s$的值实时更新
$access$的时候因为去掉了$x$之前的实儿子(变为了虚儿子),增加了一个新的实儿子(由原来虚儿子变来),$si$一加一减,$s$不变化
for (int y=0;x;x=fa[x]) { splay(x); si[x]+=s[son[x][1]]; son[x][1]=y; si[x]-=s[son[x][1]]; pushup(x); y=x; }//access
在$link$的时候增加了一条虚边,改变$y$的$si$,要注意要将$y$转到根上,以免改变其他节点的值
inline void link(int x,int y) { makeroot(x); access(y); splay(y); fa[x]=y; si[y]+=s[x]; pushup(y); }
$cut$因为切掉的是实边,对$si$无影响,注意$pushup$即可
还有这里可以维护的信息是满足加减性的,如子树大小,子树权值和
但像子树权值最大值是不满足加减性的,不能维护(据说可以每个节点将$si$和$s$改成一棵平衡树)
在求出$x$子树的信息的时候,$access(x)$后取出$si[x]$即可
8.维护区间中位数
可以二分答案,将$>=x$的数标记为$1$,$