CF1045J Moonwalk challenge

这题怎么才 2600 啊,我觉得有 3000+,太菜了 /ll。

明天期中考试了,来一个官方题解做法涨涨 rp,复杂度更劣还要离线,被爆了 /ll。题解区大佬说哈希狗都不写。

洛谷 CF

  • 给出一棵 n 个点的树,边上有字母。q 次询问,每次给出一条路径 uv 和一个字符串 si,询问 siuv 的路径边上字母顺次拼接构成的字符串中出现了几次。

  • n,q105,记 S=maxi=1q|si|S100

看到这题,我的第一反应是 CF1608G Alphabetic Tree,两题难度虽然差的有点多,做法的相似之处也不是很大,但是都是十分恐怖的重工业题。

进入正题,我们发现询问串的长度很小,考虑分别处理每一种长度的询问,记当前正在处理的长度为 len

先对树进行重链剖分、边转点,则路径由 O(logn) 条重链组成。对于每一个询问,可以将字符串的出现位置分为完全在重链内以及跨越重链两类。

  • 对于完全在重链内的位置:
  • 先处理出每条重链自上而下以及自下而上前缀哈希值,这样就可以求出重链内任意一条子路径、任意一种方向的哈希值。

  • 求出每个点向上走 len 条边,向上、向下的哈希值 vui,vdi,并将它们离散化。对于每一种离散化值维护一个 std::vector 按顺序从小到大存放哈希值离散化后为该值的点的 dfn

  • 则跳链的时候,设当前的链底的点为 x,则问题可以变成:

    查询在区间 [dfntopx+len1,dfnx] 内,有多少点的权值为给定的值。

  • 类似 P5838 一样,在 std::vector 上二分得到左右端点求区间长度即可。

  • 若当前重链在 uLCA 的链上,则查询向上的哈希值,否则查询向下的哈希值。

  • 对于跨越重链的位置:
  • 在处理“完全在重链内的位置”时,用 std::vector 存下每一条重链的顶部、dfn 区间和方向。

  • 枚举起始的重链,则出现位置一定是起始重链长度为 len 的后缀(若不足则全部选,下同),以及该重链结尾位置再往后走 len 条边构成的字符串中(不然出现的起始位置要么没有跨越重链,要么不在起始重链内)。

  • 暴力找出这条路径并记录其构成的字符串,进行哈希 / KMP 与询问串进行匹配。注意要保证跨越重链及起始位置在起始重链内。

做法大概就这样,好像也不是 too hard。接下来算复杂度。

  • 输入以及重链剖分的预处理部分时间复杂度为 O(n+qS)

  • 对于枚举 len,预处理每个点向上 / 向下的哈希值并离散化的部分,一个点计算一次时间复杂度为 O(logn),要排序的元素一共有 O(nS+q) 个,总时间复杂度是 O((nS+q)log(n+q))

  • 对于一个询问,处理“完全在重链内”的位置的时间复杂度为 O(log2n);处理“跨越重链的位置”时,枚举重链的时间复杂度为 O(logn),找出路径以及字符串匹配的时间复杂度为 O(S)。所以一次询问的时间复杂度为 O(logn(logn+S)),总时间复杂度是 O(qlogn(logn+S))

  • 综上,该算法的时间复杂度为 O((nS+q)log(n+q)+qlogn(logn+S)),常数有点大。配合 6 s 的时限还算能够接受。精细实现后跑得很快。我人傻常数大。

  • 空间复杂度显然为 O(n+q)

至此这题基本上解决了。完结撒花!

提交记录(含 8.9KB 抽象代码)

posted @   lzyqwq  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示