Live2D

Solution Set - “一二行诗句相遇,十万颗恒星解体”

Defining LATEX macros

0.「集训队互测 2018」Fim4 ⭐

  暂时不知道正解, 也不知道兔写了依托啥玩意儿. (

  询问串离线建出 AC 自动机, 我们需要将母串在 AC 自动机上匹配, 最后在 fail 树上求子树和得到答案. 母串长度 5×109 不算太长, 我们可能可以压位来节约一个常数级别的复杂度卡过.

  取步长 w, 对于 AC 自动机上的每个结点, 处理其 2w 种转移到达的结点, 匹配时只在 2w×k 步到达的结点打标记, 这样匹配过程就可以除上一个 w. 此后, 我们需要对 AC 自动机上的结点释放标记, 类似搜索地实现可以做到单个结点 O(2w) 释放标记. 复杂度 O(|S|2w+n|S|/w), 主要瓶颈是前一个部分的空间, 这里取 w=10 可以卡过. 当然兔释放标记的部分写的真的是搜索, 如果把这个东西展开成循环应该会快上不少.

1.「ABC 294Ex」K-Coloring ⭐

  题如其名, kcoloring 板题, 这个问题的一个表现较优秀的写法是采用缩图策略:

  • 若图不连通, 递归求解.
  • 若存在叶子, 答案乘上 k1, 删掉叶子及其连边, 递归求解.
  • 若存在度数为 2 的点, 讨论其两侧结点颜色是否相等, 递归求解.
  • 若上述缩图策略无效, 用子集卷积暴力求答案.

  毛估复杂度, 当图无法收缩时, 每个结点的度数 3, 则结点数 2m/3. 因此, 暴力部分复杂度 O(22m/3m2), 容易看出其余部分不是瓶颈, 这样就得到了一个 O(1.59mm2) 的算法. 当然, 通过更精细的分析, 可以得到更紧的界.

  不好写? 一个 sweet 的写法是, 先判连通性, 然后取度数最小的结点, 容斥其邻接边颜色取等情况. 这种方法的递归 "底层" (无法缩图的情况) 复杂度略高 (吗?), 但的确舒服很多.

2.「NOI Simu.」解码

rep (t, 0, cur - 1) {
    if (same(i, t, cur, stpn)
    && same(i, (t + 1) % 10, cur, stpn)) {
        return true;
    }
}

  你说得对, 但 tcur 是第 k 位而不是第 i 位. 你真会写代码.

  第一个想法是二分答案, 那就一条路走到黑吧! 对于二分的答案 t, 我们考虑检查是否存在一个 mn 使得 n,m 的前 t 步对应相同的字符串.

  比较 key 的 observation 是, 设 nt 步内产生过字符变换的最高的位为 h, 则 n,m 的前 h 位 (第 0 位至第 h1 位) 必然相同. 我们从 h 往上枚举 n,m 第一个不同的位以及 m 的对应取值, 注意取值是越小越好, 这样可以尽量避免产生 "m 先进位, n 再进位" 的情况, 减少对字符表的要求. 如果构造过程中已经使得 m<n 那就一定有解了 — 取 m 的高位和 n 相同即可. 贪心判定, 最终复杂度是 O(b22logn), 其中 b=10.

3.「NOI Simu.」图 ⭐

  差不多会了, 写出来的是:

if (sum[rt] != 2 && sum[rt] != clr[rt]) {
    rep (u, 1, n) if (u != rt && clr[u] == clr[rt]) {
        clr[u] = 2; // Stimulate an adaptation (always exits).
        for (int v = u; v != rt; v = fa[v]) {
            sum[v] = 0;
            for (auto [w, i]: tre[v]) if (w != fa[v]) sum[v] += ans[i];
            sum[v] %= 3, ans[fe[v]] = (clr[v] - sum[v] + 3) % 3;
        }
        // break;
    }
}

注释行没有. 乐.

  先判断掉 n=2 无解, 因为题目中给了很多限制 (三部图, 连通), 我们可以坚信剩下的情况都有解. 先来考虑二分图的情况. 图上构造的常见思路: 取一个生成树, 我们就在生成树上调节度数. 假设在一种二染色方案中, 点 u 的颜色是 cu{0,1}, 一个自然的想法是构造边权使得 u 的边权和 su=cu. 在树上, 我们可以轻松地调节好除根以外的结点, 如果此时根 r 非法, 怎么办呢? 注意我们只用了两种颜色, 接下来就需要引入 c=2 了. 我们取 urcu=cr, 令 cu2, 对应调整 ur 这条路径, 此后 sr 必然被改变, 不管变成什么都是一定合法的!

  好, 放到三部图上, 我们仍然尝试用树构造 su=cu. 与二部图不同的是, 我们需要用树外的边构造答案. 非二分图一定存在奇环, 奇环上的边交替 ±1 就可以让特定的点 susu+2, 我们用某个奇环调整根结点即可.

  两部分复杂度都是 O(n+m).

4.「NOI Simu.」表达式

  这个… 有点怪啊, 猜猜出题人的 motivation 好了.

  首先, 这个问题不弱于 2-SAT, 且 SAT 结构整出来的 DAG 可以等价地描述变量关系, 我们不妨就在图上思考. 所有量词都是 时, 可以导出原命题为真的必要条件:

  • i, xix¯i 不再同一个 SCC 中.

  需要研究的是全称量词, 我们可以枚举一下两个量词之间的位置关系:

  • (xi)(xj), 就是上面的东西.
  • (xi)(xj), 如果 xixj, xix¯j, x¯ixj, x¯ix¯j 任意一者的原命题或逆命题为真, 则原命题为假. 特别地, i=j 时, 仍然有同样的约束.
  • (xi)(xj), 如果 xixj, … 任意一者为真, 则原命题为假. 虽然初始的说法不长这样, 但经过一些化简可以得到这个结果.
  • (xi)(xj), 没有额外限制, xi 已经在 (xi) 约束过, xi 确定之后, xj 一定存在.

  最后, 我们惊讶地发现这些限制都很好判断, 甚至不需要求 DAG 可达性. O(n+m) 结束.

5.「ULR #1」「UOJ #577」打击复读 ⭐

6.「UR #7」「UOJ #84」水题走四方 ⭐

  不管是暴力 DP 还是研究结论, 我们必然要先来约束这个超高自由度的移动操作. 可以发现, 我们可以构造方案使得其中一个人从来没有传送向另一个人, 我们把没有传送过的人叫做本体, 另一个人叫做喻体分身, 那么, 整个操作过程就是: 本体在一条树链上行走, 分身去处理所有的分支子树.

  进一步的, 本体和分身的一些行动可以并行: 当分身只需要处理一条链时, 本体就不需要站在原地传送分身, 而可以继续向下走. 当然, 取子树中最长的树链一定最优, 这给本体预留了最多的行动时间. 麻烦的是, "子树最长链" 需要排除本体走的链, 我们根本不知道这条链是什么呀!

  贪心不了了, 我们尝试 DP. 如果知道本体在并行行走结束时站的位置, 我们就能求出分身处理的子树, 继而得到答案. 因此, 设 f(u) 表示处理好了 u 子树外的所有点, 现在本体和分身都站在 u 上时, 处理剩余部分的最短时间. 我们枚举第一次并行结束时本体走到的结点 v, 则 sub(u)sub(v) 中需要存在一个深度不小于 v 的点 (这样才能并行), 有转移

f(v)minf(u)+susv(cucv)du,

其中 su 表示 u 子树内叶子深度和, cu 表示 u 子树内叶子个数, du 表示 u 的深度. 当然, 当 vu 的唯一儿子时, 有非并行的转移:

f(v)minf(u)+1.

那么 minuleaf{f(u)} 就是答案.

  DP 优化倒是好说 — 并行转移中, v 肯定只需要被最近的可转移的 u 更新. 我们只需要对每个点 u 求出这个祖先 au 即可. 仍然是按子树处理, 递归完子树后剩下未被求解的 au 组成链表, 合并时交叉更新就行. 复杂度 O(n), 尽量不要引入递归.

7.「UR #8」「UOJ #118」赴京赶考

  横纵坐标独立, 距离是区间内连续段数量. 注意是在环而非链上行走. O(n+q).

8.「UR #8」「UOJ #119」决战圆锥曲线

  圆锥曲线是断然不会算的, 我们不如找一点简单的支配对来剪枝, 例如, 若 (i,yi)(j,yj) 满足 i<jyiyj, 则 (i,yi) 断然不可能参与答案贡献.

  等等, 序列随机, 这个后缀最大值的数量是 O(logn) 的! 我们直接线段树维护区间最值, 然后树上遍历出所有后缀最大值更新答案即可. 复杂度 O(n+m1logn+m2log2n), 题目中有 m2105.

9.「SDOI 2013」「洛谷 P3303」淘金

  最暴力的暴力就是最正解的正解, 不愧是你.

  显然横纵坐标独立, 设 g(i)=|{j[1,n]f(j)=i}|, 我们如果求出所有的 g, 剩下的工作就是求 g(i)×g(j) 的前 k 大, 这个倒好说, 拿堆随便算算就行.

  怎么求 g 呢? 注意到 g(x)0x 必然有 x=2a3b5c7d, 毛估不过 105, 跑出来差不多 104, 直接用 std::map 记忆化所有状态跑数位 DP 就行. 设状态数为 S, 复杂度为 O(bSlogS+klogk), 其中 b=10.

10.「ZJOI 2019」「洛谷 P5327」语言 ⭐

  • Link & Submission.
  • 「A.树论-虚树」「A.启发式合并/分裂」

  路径交叠特别复杂, 跨子树根或者跨分治中心计数都不太方便, 我们可以考虑另一种计数策略: 对于每个点, 求出其能够开展贸易的点数.

  对于点 u 答案的求解, 我们只需要保留所有经过点 u 的路径, 求出所有这些路径端点构成的虚树, 虚树 (对应于原树上) 的大小就是答案. 注意答案本身之和虚树形态有关, 而这样的虚树可以通过树上差分删除/加入单点, 启发式合并点集来维护, 这道题就这样了. 复杂度 O(mlog2m).

11.「ZJOI 2019」「洛谷 P5328」浙江省选 ⭐

  注意到 m 很小, 所以我们的算法大抵是一层一层地大力找到能当第一的一次函数, 把它们删除, 如此迭代. 当然, 求能当第一名的函数并不复杂, 就是求这些函数构成的下凸包, 在计算几何的语境下可以说是半平面交, 不过全部是 k,b>0 的一次函数, 单调栈扫一扫就行.

  求出凸包后由于各个位置被已经去掉的函数的覆盖次数是不一样的, 我们需要拿已经被剥离的上层函数在当前凸包上二分出覆盖部分, 打差分标记, 最后扫描凸包查看凸包上每个函数的露出部分是否存在一个整点, 该整点只被覆盖了层数 1 次, 如果存在这样的整点, 对应的函数就进队 (迫真) 了.

  复杂度 O(nmlogn).

12.「UR #8」「UOJ #120」宿命多项式 ⭐

  尝试通过枚举系数来满足限制. 不妨来计数整系数降幂多项式 f(x), 使得 f(i)[0,ci)N, 不难发现这和原问题等价. 设

f(x)=i=0naixi_,

那么

0a0+ka1+k2_a2++kk_ak<ck(k).

设前面的部分为 dk, 则

0dk+k!ak<ci.

可见, 我们关心 dkmodk! 的值. 这里比较 key 的转化是, 我们可以通过预先构造 ak=(nk)!pk+qk 来降低信息量. 假设我们枚举了 {qn}, 令 Mk=k!(nk)!在这种构造下,

0dk+k!((nk)!pk+qk)<ckcount(pk)={ck/Mk,ckmodMk(ck+dk+k!qk)modMkck/Mk+1,otherwise.

其中

dki=0k1ki_((ni)!pi+qi)i=0k1ki_qi(modMk).

可见, 的确只需要 {qn} 就能计算方案数了. 直接枚举的复杂度是 O(1!2!n!n).

  你妈的, 怎么还有 q 次询问啊?

  尝试把 n! 这层枚举去掉. 好消息是, k0_q0=q0, 因此 q0modMk 的影响是一个匀速的转圈, 总共只有 O(2n) 个发生取模的点, 在两个相邻取模点之间的答案贡献恒定, 这样就能做到单次 O(1!2!(n1)!2n) 啦! 不过毕竟还得排序, 所以可以若干个序列统一进行 n! 值域的桶排.


  赞叹一下现代编译器, 编译期求值展开所有预处理过程就不说了, 对小数组的循环迭代貌似会倍增地展开成顺序结构 (比如对 int ary[7] 的迭代, 会生成迭代 4,2,1 次的汇编, 然后判断跳转). 但不知道为什么, 我的提交的代码在 g++ 10.3 中开启 -O3 选项时会警告:

.../main.cpp:66:38: warning: iteration 7 invokes undefined behavior [-Waggressive-loop-optimizations]
   66 |     rep (j, 0, k) val[k] += dpwr[k][j] * rem[j];
      |                             ~~~~~~~~~^
.../main.cpp:5:52: note: within this loop
    5 | , l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
      |                                       ^

.../main.cpp:66:5: note: in expansion of macro ‘rep’
   66 |     rep (j, 0, k) val[k] += dpwr[k][j] * rem[j];
      |     ^~~

而且相同的警告重复了三次 (难道对应了上面的倍增次数? 阅读汇编代码发现这个循环似乎确实被倍增展开了). 不知道为什么会有这个警告, 逻辑上 j 也不可能 =7 啊.

13.「NOI Simu.」乐 ⭐

  乐, 复杂度感知对了也除不下去 ω.

  经过足够时间的试错之后可以发现这题不太能 polylog (实际上, 似乎可以归约矩乘证明), 我们不得不在 106 的震撼规模下考虑根号分治, 继而可以猜到一个类似 O(nn/ω) 的复杂度 — 我们只需要在某种块长上除掉一个 ω, 就可以平衡出正解.

  设阈值为 B, 对于询问 (s,t), 若 deg(s)B, 该询问最多在 O(B) 种颜色的连通块中有意义, 我们可以 O(nB) (n,m,q 同阶, 下同) 地查询它们.

  现在, 我们只需要讨论满足 deg(s),deg(t)>B 的询问. 注意这样的点只有 O(n/B) 个, 我们称它们为关键点. 对每种颜色, 考虑其所有连通块, 若连通块大小不超过 B, 我们还是可以 O(B2n/B) 地大力更新答案 (一共只有 O(n2/B2) 个答案是我们关心的, 可以直接保存). 否则连通块大小大于 B, 这样的连通块总共只有 O(n/B) 个, 我们给这个连通块一个新的标号, 将其记录在其中的每个关键点上. 最后查询时, 我们需要额外加入这部分贡献, 即两个关键点的标号集合的交集大小, 用 std::bitset 维护即可. 这部分的复杂度是 O(n2/(Bω)).

  取 B=n/ω, 平衡得到 O(nn/ω), 不过空间是 O(nω) 的, 实际实现时阈值不能太小.

14.「NOI Simu.」铲雪 ⭐

  (这个星星纯粹是对代码量的反抗 w.)

  一眼丁真, 鉴定为道路铺设. 边上权值道路铺设的结论很简单, 我们知道

ans=u=1nwumax{2xusu,sumod2},

其中 suu 邻接边边权和, xu 是其中的最大边权, 这个式子很好理解, 难点在于维护.

  当 2xusu 时, 我们定义此时 xuargmax (之一) 为 u 的 dominator, 记为 dom(u), 若不存在这样的点则 dom(u)=0. 注意到若一个点 u 的两条邻接边同时被 +d, dom(u) 只可能从非零值变为 0, 若 u 只有一条邻接边被 +d, dom(u) 才可能从 0 变为非零值. 也就是说, dom(u) 的全局变化次数是 O(n) 的. 我们可以树剖剖分, 将 dom(u) 非零, 且非父亲, 非重儿子的 u 拉到线段树上维护贡献和 2xusu 的最小值, 用于检测 dom(u) 变化; 其他的 u 大力维护答案就行. 复杂度 O(nlognlogm).

15.「CF 1310C」Au Pont Rouge

  写一个 O(n2log2n) 甚至 O(n2logn) 的玩意儿 (n,m 同阶), 全程暴力可过.

posted @   Rainybunny  阅读(154)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示