集训总结

· · 个人记录

没啥想补的题了,写写这一个月集训学了啥(不然忘了=白学)。昏迷着写的,喷轻点。

别人初三在学(我们还没系统学/kk)。

普通的整体二分就是把操作在一维上分治(保证分治的层数为 \log V),分成两部分 S,T,满足在分治那一维当 T 产生贡献时 S 也产生了贡献。然后把 S 的贡献算出来,如果询问在 S 的贡献中产生,那就把询问分到 S 那一边,否则去除 S 贡献,分到 T 那一边,然后递归求解,复杂度的话分治的时候保证了深度,那外层的复杂度就只会多一只 \log V

举个例子:单点修改,区间 k 小(Dynamic Rankings),按值域分治,把初始的序列看成在一个点插入上一个值,修改看成删除原来的值,再插入一个值,这样得到的 O(m) 个操作拿来分治,这里的修改和询问混一起更好,因为对于一个询问,S 造成的贡献还受时间限制,所以要按时间遍历(当然你要麻烦的归并一下我也拦不住你),修改的时候如果插入/删除的数在 S 里,就在一个树状数组里改,询问就区间求和,如果 <k,那这个询问就是在 T 集合中求得答案,否则是在 S 集合中。然后就可以分成两个操作序列,递归分治。

当然还有不太一样的整体二分。栗:WD 与地图。这个题的整体二分不是用来求解的,而是用来转化边的,因为插有向边,求强连通分量这个很麻烦,所以直接在时间轴上整体二分,然后暴力 tarjan 求连通性,在强连通分量里的边会在前半段时间中产生贡献,所以为 S 部,前半段时间中剩下的边和后半段时间的边为 T 部。

有些细节在于直接把边递归到 T 不好再跑 tarjan,所以要把端点的标号改成这一层强连通分量中的一个代表点。为啥要这样?因为要的是每个时间哪两个集合合并了,而这个是用并查集,所以在 T 部中可以直接看成把连通块缩到那个代表点上了。

这不就是更好记的广义容斥吗?

形式一:

f(n)=\sum\limits_{i=0}^n{n\choose i}g(i)\leftrightarrow g(n)=\sum\limits_{i=0}^n(-1)^{n-i}{n\choose i}f(i)

f(i) 看成至多 i 个的方案数,g(i) 看成恰好 i 个的方案数,然后就可以转化了。证明的话就是互相带入,可以二项式定理化简。

形式二(广义容斥):

f(n)=\sum\limits_{i=n}^m{i\choose n}g(i)\leftrightarrow g(n)=\sum\limits_{i=n}^m(-1)^{i-n}{i\choose n}f(i)

插叙:机房大佬觉得这很广义容斥,于是他手搓了一个五个圈的韦恩图来解释。

附插图

同理把 f(i) 看成至少 i 个的方案数即可。

栗:情侣?给我烧了!

经典被暴力递推爆踩的式子题。

恰好转至少,考虑第二种形式,那就转化成了 f(i) 怎么求,考虑组合意义:选 i 对出来,选 i 个位置,i 对里男女顺序任意,剩下的任意。然后就很简单了。自己推推吧。

机房存在 wqs 本人

对于一个函数 f(k),单独求一个点值很不妙,但是可以给一个费用 w,然后求最优的 f(i)-w\times i 以及他的 i(都是定义在整数上的)。如果这个函数是有凸性的,那二分这个 w 就相当于二分他的导函数,显然有单调性,于是二分到 i=k 的地方就是答案了。

tricky,但是细节很麻(如果自己摸索的话)。

题目一般二分只是外层且是最不重要的一环,所以没有例题。

容斥的形式套上了 MinMax,就是这个了(这个形容很完美)。k 小貌似差不多,写个通式:

max_k(S)=\sum\limits_{T\subseteq S,|T|\ge k}(-1)^{|T|-k}{|T|-1\choose k-1}min(T)

不知道为什么 \max_k 挂了,所以写的 max_k。

然后就是期望形式,注意是 E(max_k(S)) 而不是 max_k(E(S)),这真是个逆天新手坑。

栗:随机游走

其实转化是板子,重点是树形 dptrick,复习一下。

至少几步:令 S 表示每个点被走到的时间,E(max(S)) 即为答案,容斥一下,变成求第一次走到 T 中点的期望步数,这个很好 dp

若当前枚举的集合为 S,则待定 f_u=A_uf_{fa_u}+B_u,然后转移式为:若 u\in S,则 f_u=0,否则 f_u=\frac{1}{deg_u}(f_{fa_u}+\sum\limits_{v\in son_u}f_v)+1,把 f_v 都带成 A_vf_u+B_v,然后就可以解出 A_u,B_u,在树上转移了,边界注意判一下(u\in S 时,A_u=B_u=0)。

树上相邻节点互相转移的自动机应该都可以这么做。

我也不知道什么黑科技,在求函数 f(k) 的任意点值时,若 f(k) 有凸性且两段的贡献互相独立,就可以分治求解,然后闵可夫斯基和一下。这个合并合并的是凸包里每个线段,按斜率合并就行,可并堆很好。

简单的可并堆:随机偏树,在每个点操作前把左右儿子随机交换(合并就相当于随机挑一个子树合并进去),期望 \log n

《板题太水,难题不会系列》

讲道理,暴力分讨封装一个求 ax+by+c=0 的函数和求交点的函数就好了。

叉积是 a_xb_y-a_yb_x,正负性可以右手摆一摆,可以判断点在线段哪边。

多边形面积可以随便选个点,求一圈叉积,取绝对值。

考不到很难,不写了

数据结构,很好,很好。

众所周知,树剖有个很舒服的东西就是你对每个点维护其轻儿子信息,然后用 dfn 序维护重链信息,轻边用 dfn 序求,可以得到很好的询问子树信息的做法,所以这也可以照搬到 dp 上。

考虑树形 dp 的时候转移的东西(以最大独立集为例):f_{u,0}=f_{u,0}+\max(f_{v,0},f_{v,1}),f_{u,1}=f_{u,1}+f_{v,0},用广义矩阵写就是 [f_{u,0},f_{u,1}]=[f_{v,0},f_{v,1}]\times\begin{bmatrix}f_{u,0}&f_{u,1}\\f_{u,0}&-\inf\end{bmatrix}

好像这个还不太好扩展,那如果我们的 f_{u,0},f_{u,1} 已经把轻儿子转移完了,这样就得到了一个 O(2^3) 向上跳一个点的转移,可以发现轻边不能这样转移,但是我们并不需要轻边,只需要询问的子树的根所在的重链的矩阵就行了。

具体来讲,把轻儿子都转移了相当于 g_{u,0}=\sum\limits_{v\in son_u\land v\neq x}\max(f_{v,0},f_{v,1}),g_{u,1}=\sum\limits_{v\in son_u\land v\neq x}f_{v,0},其中 x 为重儿子,g 为转移了轻儿子后的 dp 数组。这样的话改一个点的权值,可以改他的 g_1,然后求出该重链顶端为根的 f 的变化,去更新重链顶的父亲的 g 值,然后依次执行直到整棵树的根。

具体细节可以自己摸索,这个打起来不思考一下怎么写的话有点麻烦。

全局平衡二叉树

提到 ddp,提到树剖,怎么能少了这个 B 东西呢?

上述做法的复杂度为 n2^3\log^2 n,看着对 10^5 都不太友好(但是树剖天然常数小),更何况加强版。该做法的复杂度有两只 \log n 都是在求多条重链信息的,而套的线段树只是无脑的套了上去,这并不优雅,也就是一点不考虑平衡复杂度的问题。

想想重剖为什么是 \log n 的外层复杂度,因为每条轻边都会让子树的大小翻倍,那在一条重链上跳只要我们让他满足这个条件是不是每跳一下都是翻倍,总的复杂度(加上区间查)也只有 \log n 了呢?具体来讲就是对重链加权建一颗平衡树,使得每个点的左右两个儿子权值和之差尽量小,加的权就是每个点的轻儿子大小之和。

这样重链的一段前缀就可以从最下面的一个点开始往父亲跳,然后画一下图就可以知道哪些节点的值可以表示这个区间。

考虑对一张图,度数矩阵有 D,D_{in},D_{out},邻接矩阵 A

对于无向图的生成树个数为 D-A 去掉 ii 列的行列式的值。

对于有向图的内向生成树,若以 i 为根,则是 D_{out}-A 去掉 ii 列的行列式的值。

对于有向图的外向生成树,若以 i 为根,则是 D_{in}-A 去掉 ii 列的行列式的值。

应该没有记错。