2024.01.25 近期练习

CF1508D

经典套路,把 \(i\)\(a_i\) 连边,形成若干的置换环。
注意到我们对于一个环,可以令其变成一个菊花,即不断地对一个点操作消去出边。
如果只有一个环,那么问题轻松解决。
如果有多个环呢,交换其中两个环中的一对就可以变成一个环。
把剩下的点进行极角排序,然后相邻的两个点进行操作:
如果两个点所在的环还没有并在一起就交换这两个点,用并查集维护。
发现这样是不可能与菊花相交,因为这些边都在菊花相邻边之间。
最后再做一遍开头那个算法即可。
这种问题首先应处理排列,关于相交的问题可以用极角排序。

CF1491H

如果没有修改,我们可以借助“弹飞绵阳”的套路,分块,维护跳出这个块到哪。
先考虑查询的话,我们可以像类似树剖那样向上跳,复杂度是 \(O(\sqrt n)\) 的。
修改怎么办?对于散块来讲直接暴力重构。
整块怎么办?由于修改只有 \(-x\),如果 \(x\) 之和超过块长,那么所有元素一跳就出块了。
所以整块也暴力修改。注意到一个块只会被修改 \(\sqrt n\) 次,复杂度是 \(O({\sqrt n})^3=n^{1.5}\)
当一个块超过 \(\sqrt n\) 修改时,直接打个标记即可。
这种大数据结构应考虑分块思想,以及考虑势能分析。

CF1856E2

如果 \(n\le 5000\) 考虑怎么做。
首先我们对于每个节点只考虑大小关系,最后只需要从小到大标号即可。
我们考虑把答案放到 LCA 处统计。
若其只有两个儿子 \(v_1,v_2\),那最多只有 \(siz_{v_1}\times siz_{v_2}\) 个会被统计,即令 \(v_1\) 所有数大于 \(v_2\)
若有多个儿子,就是把儿子子树的大小分成两个集合,使得两个集合差最小。
可以用 01 背包解决。然而 \(O(n^2)\)。考虑 \(n\le 1e6\),如何优化 01 背包?
可能有关 bitset。注意到背包所有元素的和是 \(O(n)\)。所以不同大小的个数是 \(\sqrt n\) 的。
不妨二进制拆分做 01 背包,复杂度是 \(O(\frac{1}{\omega}n\sqrt n \log n)\)
此外,如果有一个儿子大小超过一半,是不用做背包的,这保证了复杂度。
所以树的 \(siz\) 是分一半最优,总复杂度可以用主定理计算。
实现的话,需要使用动态大小的 bitset,
可以手写 bitset,或提前开好所有 \(2^k\) 大小的,用的时候用一个最合适的。
这种问题有关排列,应考虑相对顺序。再转化为背包,应想到 bitset,再观察背包的性质。

CF1764F

连接 \((i,j)\),就意味着把 \(i\to j\) 的路径作为环。\(f_{i,j}\) 就是其他点到这个环的距离和。
可以求出每个点的一条出边,也就是若 \(j\)\(f(i,j)\) 除了 \(i\) 以外最大的,那么存在 \((i,j)\)
注意到若 \((X,Y)\) 包含 \((x,y)\) 路径,那么 \(f_{X,Y}<f_{x,y}\)
显然,若环的大小越大,那么每个到这个环的距离和就越小。
所以我们可以断言原树的形态是以 \(f(i,j)\) 为边权的最大生成树。
当树的结构被确定后,那么边权也很容易。
钦定 \(1\) 是根,\(w_{i,j}=\dfrac{f(i,i)-f(i,j)}{siz_j}\)
这种问题要联想到生成树。

CF1827C

关于回文先想到使用 Manacher 算法。
我们借助 CSP2023T2 的算法,设 \(f_i\) 表示以 \(i\) 开头的回文串数量。
\(f_i=1+f_j\)\(j\)\(i\) 右边第一个满足 \([i,j]\) 回文的位置。
问题转化为求右边第一个回文的位置,即求每个位置右边第一个包括它的回文中心。
考虑从左向右扫描,然后每扫到一个回文重心,就更新前面的。
也就是区间取 \(\min\),想到可以用吉司机线段树。
然而是不必要的,考虑并查集优化,并查集维护 \(r_i\) 表示右边第一个还未更新的。
那么直接暴力即可,因为每个点只会被第一次更新。
由于只有一次被更新,所以复杂度是 \(O(n)\) 的。
这种问题需考虑 DP,以及并查集优化。

CF1887D

我们发现正面去处理这个问题是很难的。
转化题意,由于有 \(\min\) 也有 \(\max\),枚举其中一个 \(\max\)
考虑枚举切割点 \(a_i\),使得 \(a_i\) 是左边部分的最大值,且右边都大于 \(a_i\)
\(a_x\) 是左边第一个大于 \(a_i\) 的,那么 \(x<l\le i\)
\(a_y\) 是右边第一个大于 \(a_i\) 的,\(a_z\)\(y\) 后第一个小于 \(a_i\) 的,那么 \(y\le r< z\)
那么这个问题就很容易地转化为了矩阵加法,可以直接扫描线解决。
那么查询就是单点查询罢了。
这种问题就是需要反方向考虑,之后就是扫描线模型了。

CF1904E

考虑离线询问。
换根 dp,用线段树动态地维护深度,问题转化为最深的点是哪个。
考虑 \(k\) 个点带来的限制,即把这 \(k\) 个点子树化为 \(0\),用 dfs 序即可,dfs 序是可以换根的。
具体地,可以给这些区间打上一个标记,到时候还原即可。
这种问题首先考虑离线,树上的问题可以换根 dp。

CF1919E

考虑把正数和负数分开来考虑,形成 0-正-0-正-0-负 这样的结构。
正数的话考虑从大往小填数,设 \(f_{i,j}\) 是已经填了 \(\ge i\) 的数,形成了 \(j\) 个连续段的方案数。
当我们加入 \(i\) 时,我们对于已经形成的连续段,可以在两边各填一个 \(i\)
也可以在两个连续段之间填一个 \(i\),连接这两个段。
注意到一个段如果右端点是 \(n\),那么右边就不用填了。
我们设计状态 \(f_{i,j,0/1}\) 表示已经填了 \(\ge i\) 的数,\(j\) 个连续段,是否出现 \(n\) 的方案数。
\(f_{i,c_i-j,0}\gets f_{i+1,j,0}\times C_{c_i-1}^j\)\(f_{i,c_i-j,1}\gets f_{i+1,j,0}\times C_{c_i-1}^j\)
\(f_{i,c_i-j+1,1}\gets f_{i+1,j,1}\times C_{c_i-1}^{j-1}\),即插板法。
负数同理,最后把正数和负数合并起来即可,同样插板法,把 \(0\) 插进去。
这种问题要设计好状态,考虑“插入dp”。

posted @ 2024-01-26 21:56  s1monG  阅读(30)  评论(0)    收藏  举报