讲题记录

记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存记得保存

1.21 树专题 zz

CF1578J Just Kingdom \color{blue}\Diamond

首先分析这个操作造成的影响。容易发现这样对于一个点 uu,可以把它的儿子分成两类:要么以这个儿子为根的子树内所有点的限制都被满足了,要么没有。对于前者就是填满,后者会平均分剩下的。

那么此时就有一个 O(n2)O(n^2) 的暴力:对于每个点,从它往上跳并维护当前需要的钱 limlim。转移是 limvsonumin(sizv,lim)lim\leftarrow \sum_{v\in son_u}\min(siz_v,lim)

考虑优化。\color{blue}\Diamond 对于这种将一个值变为和其他某个值相关的 min\min 的东西,可以考虑当 min\min 去到它本身时,它的值至少翻倍。而注意到 sizvnVsiz_v\le nV,所以这样的关键点只有 O(logn+logV)O(\log n+\log V) 个。

后面的实现部分不是重点,倍增预处理一些东西,查询时二分即可。

CF1423C Dušan's Railway

构造题。

首先题目给的这个限制太奇怪了,尝试进行一个翻译。一个很不知道怎么样得到的想法是,你要构造一种存储 10n10n 条路径的数据结构,使得任意一条树上的路径都能用其中的不超过 33 条拼出来。

树太困难了,考虑序列。如果限制是 22 条的话,显然可以想到分治,进而想到猫树。但是 2nlogn2n\log n 还是太多了点。但是这里是 33 条,发现一般的分块查询就是一个块的后缀+若干个块+一个块的前缀正好 33 条。然后对于每个块递归做。

可以证明递归不超过 O(loglogn)O(\log\log n) 层,虽然我不知道怎么证。每层 52n\frac 52n 条边。

放到树上,树分块。复杂度不变,但是可能常数大一点,但是这个上界卡的不死。可以通过。实现方法是在当前连通块 sizBsiz\ge B 时分出去。

qoj9905 哈夫曼树 \color{blue}\Diamond

最重要的显然是找到一个可维护的条件,表示出这个树是否合法。

然后就有一个非常厉害的条件:\color{blue}\Diamond 将所有非叶节点的左右儿子对应权值对应的二元组 [x,y)(xy)[x,y)(x\le y) 提取出来,对二元组排序,如果两两间无交则有解。

充分性考虑可以按照排序后的顺序进行操作构造出一组解。必要性考虑若不成立,有交的两组中小的两个应该一起操作。

但是带修。不过发现这不重要,因为这棵树的深度不会很深,否则会不满足哈夫曼树性质。上界大概是 O(logn+logV)O(\log n+\log V)。所以更新的时候暴力跳父亲即可,用 set 维护二元组。

CF1284F New Year and Social Network \color{blue}\Diamond

二分图最大匹配,首先联想到 Hall 定理。是否有完美匹配?考虑对于 T1T_1 上的一个边集 SS,断开后分成若干个连通块,与其在二分图有边的 T2T_2 上的边满足其连接的两个点不属于 T1T_1 中的同一连通块。容易发现这些边至少有 S|S| 条,故有完美匹配。

然后考虑构造。\color{blue}\Diamond 对于这样和顺序无关的,可以考虑从简单情况入手,递归到子问题。考虑每次取出 T1T_1 上的一个叶子以及其父边 (u,v)(u,v)。然后考虑 T2T_2 上的 uvu\to v 的路径。由于显然有 uu 属于 T1T_1uu 所在的连通块,vv 属于 vv 所在的,所以路径上一定有一条边 (x,y)(x,y),满足 x,yx,y 不在同一连通块。然后只需要把 (u,v),(x,y)(u,v),(x,y) 缩成一个点变成子问题即可。实现上可以在 T2T_2 上二分找到 x,yx,y

qoj2562 Fake Plastic Trees 2 \color{blue}\Diamond

第二次听了,发现并不难理解?

首先有一个显然的 dp:设 dpu,i,jdp_{u,i,j} 表示 uu 子树内断了 ii 条边,当前 uu 所在连通块的权值和为 jj 的方案数。这个 dp 的问题显然是值域非常大。考虑是否能缩小值域。注意到对于一个点 uu,由于除了它所在的连通块,其他连通块都是满足条件的,所以它所在的连通块的和的取值最多只有 i(RL)i(R-L) 种。

还不够。再进一步发掘性质。然后发现,如果对于一个 u,iu,i,存在 j1<j2<j3,j3j1<RLj_1<j_2<j_3,j_3-j_1<R-L 使得 dpu,i,j1/j2/j3=1dp_{u,i,j_1/j_2/j_3}=1,则这个 j2j_2 就是没用的,j1j_1j3j_3 中一定有一个可以替代它。但是写的时候才发现我并没有完全理解怎么证明,待补。

于是用个 vector 存可行的 dp 位置,按照树形 dp 暴力合并就行。复杂度 O(nk3)O(nk^3)

\color{blue}\Diamond 关于容量为 mm 的树上背包的复杂度为 O(nm)O(nm) 的证明:把子树分成 siz>msiz>m 的大子树和 sizmsiz\le m 的小子树。

首先对于小子树中的合并,每个点被计算的点对都只有 O(m)O(m) 个,这部分就是 O(nm)O(nm) 的。

然后,当小子树被合并到大子树时,由于每个点只会被合并一次,所以这部分也是 O(nm)O(nm) 的。

最后,大子树和大子树的合并只会发生 O(nm)O(\frac nm) 次,所以复杂度为 O(nm×m2)=O(nm)O(\frac nm\times m^2)=O(nm)。所以总复杂度也是 O(nm)O(nm) 了。

qoj894 Longest Loose Segment

写着突然发现实际上非常简单。。。

直接枚举最小值位置 pp,求出以它为最小值的边界 (l,r)(l,r)。然后考虑以此为最小值的区间可能长什么样。首先一定可以证明这个区间一定顶到了某个边界。否则考虑当前最大值在 pp 的左边还是右边,往对应的方向平移这个区间一定也合法。

然后发现如果是区间右端点卡在 pp,左端点没顶边界的情况,往左平移区间一定是不劣的。因为最大值不减,最小值变大。也就是说区间一定形如 (l,x](xp)(l,x](x\ge p),即一定包含 (l,p)(l,p)(r,p)(r,p),那么最大值就是好求的了,答案也是显然的。

具体实现是用笛卡尔树。一开始题解是直接从笛卡尔树方面入手,但是自己写的时候发现直接做也是思路顺畅的。

qoj5418 Color the Tree \color{blue}\Diamond

首先不难发现每一层之间是独立的,所以分开做。

然后有一个显然的 dp:设 dpudp_u 为处理完 uu 子树内所有第 kk 层点的最小代价。则有 dpu=min(dpv,akdepu)dp_u=\min(\sum dp_v,a_{k-dep_u})

\color{blue}\Diamond 然后注意到每次处理只关注了第 kk 层的点,所以考虑建出虚树。于是上面的转移式的后半就会变成 mini=depudepfau+1aki\min\limits_{i=dep_u}^{dep_{fa'_u}+1} a_{k-i}。其中 faufa'_u 为虚树上 uu 的父亲。于是 st 表维护即可。

qoj9840 Tree Partition \color{blue}\Diamond

可能先会考虑树上 dp。\color{blue}\Diamond 但是发现值域上连续一段这个条件相当苛刻。于是不妨转换思路,对序列 dp。

于是要刻画连通块的条件。\color{blue}\Diamond 考虑把连通块挂到最上面的点上,于是可以得到一个充要条件:区间内仅有一个点的父亲不在区间内。设 dpi,jdp_{i,j} 表示当前处理了前 ii 个点,形成 jj 个连通块的方案数。不难发现需要 O(1)O(1) 转移。

考虑若干条边 (u,fau)(u,fa_u),条件就变成了区间内只有一条边是到区间外的。然后考虑到区间外的边也有两种。u<fauu<fa_u 的称其为后向边,否则为前向边。然后分类讨论:只有一条后向边和只有一条前向边。

首先对于后向边的限制,只需要维护当前所有 fau>ifa_u>i 的后向边中 uu 的前两大值,分成两个区间即可。

但是前向边比较麻烦,不过可以考虑类似扫描线地做。考虑对于每个位置维护 fif_i 表示跨过 ii 的前向边数量,然后要做的就是从所有 fi=0/1f_i=0/1 的位置中的一个前缀转移,并且支持给 fif_i 的一个后缀加。

考虑 fi=0f_i=0 的位置,每次操作都会加入一个数或者把一段后缀去掉。直接维护前缀和即可。fi=1f_i=1 的也是类似的,操作变成了把 fi=0f_i=0 的一段后缀移过来,但是发现移过来的位置中的 mnmn,其后面原来在 fi=1f_i=1 的一定已经被全部移除了。所以直接维护前缀和即可。精细一点实现就是 O(nk+nlogn)O(nk+n\log n) 的。

qoj1288 Tokens on the Tree

终于到这题了🤩当时场上被创飞了。其实后面的计数并不是难点,最最最关键的还是如何算 f(w,b)f(w,b),尝试在看题解做题后给出一个可能相对自然的思路吧。

钦定 wbw\ge b,先手玩一些小的情况。容易发现当 w=b=1w=b=1 的时候,大部分情况 f(w,b)=1f(w,b)=1。这是因为可以通过先“让一让”的方式使情况间互相转化。

再考虑一些特殊情况,比如 w=2,b=1w=2,b=1,树为菊花。容易发现这样答案为叶子个数,即黑点在不同的叶子是不同方案。思考一下这是为什么,会发现这是因为无论如何根都是白色,黑色就无法通过了。容易拓展到一般情况:记 mxumx_u 表示断掉 uu 后形成的连通块中大小的最大值,则当 mxu<wmx_u<w 时,uu 点一定为白。那么方案数就是把 uu 断掉后 sizbsiz\ge b 的连通块数量。

写的时候突然发现有个小问题:万一这样的 uu 不连通怎么办?其实容易发现这样的情况是不可能的。手玩一下就行。

那是否,若不存在 mxu<wmx_u<w 答案就是 11?考虑一条链的情况,即使 w=b=1w=b=1,也会发现对于一种情况,把 w,bw,b 位置调换之后的情况是本质不同的。于是就有两种了。于是发现,这样子“调换位置”是一种很特殊的情况。若能调换位置答案就是 11,否则是 22。不会具体证明。

然后考虑怎么样是能调换位置的。考虑调换位置的过程,可以抽象成若干步骤:ww 先进一个子树,然后 bb 进另一个,然后 ww 先出来,最后 bb 再出来。这要求存在一个点 uu,使得 uu 有两个 sizwsiz\ge w 和一个 sizbsiz\ge b 的子树。

然后就可以计数了。由于要考虑 minmxu\min mx_u,所以拎出重心。

wmxuw\le mx_u 时,没有必须是白色的点。所以对于每个 uu 求出 seu,thuse_u,th_u 表示断掉 uu 后第二,三大的连通块大小,则 ans=1ans=1 的要求就是存在 uu 使得 wseubthuw\le se_u\wedge b\le th_u。枚举 ww 扫描线,算 bb 的贡献即可。

w>mxuw>mx_u 时,必定为白色点的形成了一个包含根的连通块。于是对于每个 uu 算以 uu 为根的子树的贡献即可。具体地,会对 w(mxfau,mxu],b[1,min(w,sizu)]w\in(mx_{fa_{u}},mx_u],b\in[1,\min(w,siz_u)]f(w,b)f(w,b)11 的贡献。直接算就行。

实现上有点困难,还要处理 w<bw<b 的情况,所以要还要算 w=bw=b 的答案之和。不过也是一样的。想清楚了就没有那么难写。O(n)O(n)

2.3 字符串

CF1827C Palindrome Partition

“接上一个偶回文串”这个操作让我们想到 dp。设 dpidp_i 表示以 ii 结尾的好串的个数,问题就是要如何不重不漏地转移,因为可能有若干个以 ii 结尾的偶回文串。

此时可以发现一个关键性质:只用考虑以 ii 结尾的最短的偶回文串。证明手玩一下不想写了,总之就是更长的一定可以拆成若干个短的。所以只用处理出来以 ii 结尾的最短的偶回文串长度即可。这个随便怎么处理都行。

CF1913F Palindromic Problem

直接算。先算减少的。考虑以 ii 为中心的极长回文串长度为 nn。那么如果改变了一个在该串中位置为 xx 的字符就会减少 min(x,nx+1)\min(x,n-x+1) 个回文子串。manacher 找出来极长回文串,需要维护区间加公差为 1/11/-1 的等差数列。二阶差分即可。

再算多的。对于一个极长回文串,如果改变后新的极长串长度变了,那么一定要改原极长串左边或右边的第一个字符,变化的长度就是原串左边部分倒过来和右边部分的 lcp。二分哈希。最后枚举所有方案算答案即可。

P10716 简单的字符串问题

首先注意 S[1,i]S[1,i] 开头结尾都有 AA,所以 AA 一定是 S[1,i]S[1,i] 的一个 border。并且容易发现有单调性:如果更长的 border 合法,那么更短的也合法。

于是考虑建出失配树,询问的时候在树上倍增,于是需要求 S[1,j]S[1,j]S[1,i]S[1,i] 中出现了多少次。容易发现存储下 S[1,j]S[1,j] 所有不重复出现位置,最多有 O(nlogn)O(n\log n) 个元素,是可以接受的。于是对于一个长度 lenlen,只用查询某一个后缀 [x,n][x,n] 第一个 lcp(S[1,n],S[i,n])\operatorname{lcp}(S[1,n],S[i,n])ii 的位置即可。从小到大处理 lenlen,二分哈希预处理出每个后缀和 SS 的 lcp。问题变成支持删数,找 pp 的后继。set 就行。

询问的时候也是二分就行。Go and learn binary search。

CF1975G Zimpha Fan Club

首先我们有个非常牛的东西:带适配符的模式串与文本串匹配。

首先考虑没有通配符。显然可以 kmp。但是有没有其他方法?有的,有的。考虑给每个字符赋一个权,那么两个字符等价于 ai=bja_i=b_j。那么考虑 s[1,n]s[1,n]t[p,p+n1]t[p,p+n-1] 匹配就等价于 aibp+i1=0\sum a_i-b_{p+i-1}=0。。。吗。发现并不是,因为如果正负号不一样就影响了。于是变成 (aibj)2(a_i-b_j)^2。拆开变成 ai22aibj+bj2\sum a_i^2-2a_ib_j+b_j^2。同时注意到这里的 ji=p1j-i=p-1,于是可以卷积。

然后加通配符。不难发现可以把通配符的权设为 00,变成 aibj(aibj)2a_ib_j(a_i-b_j)^2。一样做。


回到原题。首先发现 s,ts,t 都没有 * 的显然是简单的。对于都有的,考虑每次拎出来 s,ts,t 的第一个或最后一个字符。如果有 * 就不管了。否则如果不同显然无解,相同就直接删掉。最后两个串一定有第一个字符为 * 或最后一个为 * 的。这样显然是可以构造出来的。

否则考虑变成形如 s1s2s3...sm*s_1*s_2*s_3*...*s_m*tt 匹配。显然对于每个 sis_i 贪心地找到 tt 中最前的匹配位置最优。怎么找?显然要用上面那个东西。但是这不支持增量。怎么办。注意到如果对 ss 中长为 nntt 中长为 mm 的做,那么要不找到,要不可以确定 tt 中前 mn+1m-n+1 个位置不存在可匹配的,做一次的复杂度为 (m+n)log(m+n)(m+n)\log(m+n)这么看取 m=3nm=3n 好像更优每次取 m=2nm=2n做即可。O(nlogn)O(n\log n)

P7735 轻重边

一个非常厉害的结论:将操作变成链上打时间戳,于是注意到一条边为重边     \iff 两个端点时间戳相同。于是直接嗯线段树维护即可。

当然有更无脑的做法。这里变成黑白。重剖,考虑维护重边的颜色,轻边暴力并在询问时处理。不难发现只需要暴力处理链上的轻边、跳重链时起点重儿子对应的边以及 lca 的父边(口胡的应该没错吧)。一堆细节。

CF1930H Interactive Mex Tree

首先对于查询 min\min 这个操作,由定义知 mex(S)=min(NS)\operatorname{mex}(S)=\min(\mathbb{N}\setminus S)。于是就是需要找到一种方法找出 55 个集合,其并集为所有不在路径上的节点。

由于是在不知道是怎么想出来的所以直接放结论了。。先考虑 uuvv 祖先的情况。先令 p1p_1 为 dfn 序。先问 [1,dfnu1],[dfnv+1,n][1,dfn_u-1],[dfn_v+1,n]。发现剩下一堆 uvu\to v 链上某个点的不在链上的儿子的子树。于是令 p2p_2 为出栈序 dfe。由定义知 [1,dfeu][1,dfe_u] 包含了所有 dfni[1,dfnu1]dfn_i\in[1,dfn_u-1] 并且不为 uu 祖先的 ii。所以再拼上 [1,dfev1][1,dfe_v-1] 即可。

对于 u,vu,v 没有祖先关系的情况也是类似的,手玩一下即可得到。

CF772E Verifying Kingdom

交互题,于是考虑这个操作能干什么。考虑一个点 xx,如果询问 x,y,zx,y,z,令 d=LCA(y,z)(d=x)d=\operatorname{LCA}(y,z)(d\not=x),则如果返回 dd 最深,那么可以知道 xx 不在 dd 子树内。否则若 LCA(x,y)\operatorname{LCA}(x,y) 深则 xxdd 的儿子中靠近 xx 方向的子树,反之亦然。

然后就要考虑如何去用这个操作。换个方向考虑上面的分析,容易发现:如果能找到一个非叶子节点的两个儿子子树内各一个点,那么就能知道任意一点相对这个非叶子节点的位置。那么不断对同一个未知位置的叶子操作,就能得到其真实位置。

但是问题是这样依次确定叶子位置时,很多叶子一开始是不知道的。但是注意到我们只需要知道该叶子相对当前已知叶子的位置,不断加叶子就能得到答案。于是考虑动态维护当前已知叶子构成的虚树,每次加叶子并对虚树形态进行修改即可。

最后,现在这个做法还是询问次数还是 O(n2)O(n^2) 的。但是不难想到可以套个点分,询问次数就是 O(nlogn)O(n\log n) 的了。

P5439 永恒

说实话好像就是直接做。题意即求:

u<vx<yx,yuvdepLCA(x,y)\sum_{u<v}\sum_{x<y\wedge x,y\in u\rightsquigarrow v}dep_{\operatorname{LCA}'(x,y)}

那么套路算 x,yx,y 的贡献。首先分 x,yx,y 之间是否有祖先关系的情况讨论 x,yx,yT1T_1 的多少条路径包含。而 depLCA(x,y)dep_{\operatorname{LCA(x,y)}} 根据经典套路可以在 T2T_2 上做链加链求和。具体实现的时候似乎可以稍微容斥一下多的贡献,总之就是忘了当时是怎么写的,应该不难写。

CF1930G Prefix Max Set Counting

似乎没有比 dp 更好的处理方法。但是一个问题是 dp 枚举的顺序。但是注意到,dfs 序是进入一个子树就要把这个子树走完才能出去。于是如果当前节点有若干个儿子,那 dp 的时候一定是从 maxusoniu\max_{u\in son_i}u 小的 sonison_i 开始遍历到大的,dp 时这样 dfs 就行。

然后考虑怎么转移。首先 ii 如果能成为前缀最大值,那么它一定比它所有的祖先要大。然后考虑怎么样的 jj 能转移到 ii。分两种情况讨论:

  • jjii 的祖先。那么 jjii 祖先中的 max\max
  • 否则,jj 一定要比 ii 的所有祖先大。并且令 d=LCA(i,j)d=\operatorname{LCA}(i,j),则 jj 一定是 ddjj 方向的儿子子树内的最大值。否则在进入 ddii 方向儿子子树时,当前的前缀 max\max 一定不是 jj

然后考虑怎么维护。jjii 祖先的部分是简单的。否则,考虑在 jj 出栈时更新。先将 jj 的所有儿子对应子树的贡献去除,再加上 jj 子树的贡献。同时要求 j<ij<i,所以再用 BIT 维护即可。

2.7 贪心构造博弈 hkk

P4704 太极剑

结果发现我完全没有理解这个断环成链!!!

称切的线和圆的交点为切点。首先有个重要性质:所有绳子都被切断     \iff 绳子分开的两条弧上都有切点。证明可以考虑断环成链,变成对于每个区间 [l,r][l,r][l,r][l,r][1,l1][r+1,2n][1,l-1]\cup[r+1,2n] 都有切点。然后将 2k2k 个切点排序,实际上的切线选 (1,k+1),(2,k+2),,(k,2k)(1,k+1),(2,k+2),\cdots,(k,2k)。容易发现这样一定每个 [l,r][l,r] 都和某个 [x,k+x][x,k+x] 有交。

但是这样在圆上的问题还是太难做了一些。但是发现任意一个点一定会在一条绳子的其中一边,于是先枚举一个一定选的点,剩下的限制就全部变成断环成链后,要求某个区间内一定要选点。

容易发现虽然这些区间对应序列上的位置会变化,但是在所有的 [l,r],[r,l+2n],[l+2n,r+2n][l,r],[r,l+2n],[l+2n,r+2n] 中(也就是断环成链后 [l,r][l,r] 可能对应的区间),每一次断环成链对应的 [p,p+2n)[p,p+2n) 中会且仅会包含其中的一个。于是预处理 fif_i 表示 ii 后第一个必须要有切点的位置,问题就变成了进行多少次 ifii\leftarrow f_iii 比一开始增加了至少 2n2n。倍增即可。

fu,i=dfnv<dfnu+sizudepudepv<depu+2icu(depvdepu)fu,i=fu,i1+ftou,i1+(cnttou,i1,0cnttou,i1,1)×2i1f_{u,i}=\sum_{dfn_v<dfn_u+siz_u\wedge dep_u\le dep_v< dep_u+2^i}c_u\oplus(dep_v-dep_u) \\ f_{u,i}=f_{u,i-1}+f_{to_u,i-1}+(cnt_{to_u,i-1,0}-cnt_{to_u,i-1,1})\times 2^{i-1} \\