• 题解 LOJ2065 「SDOI2016」模式字符串


    点分治。

    考虑经过当前分治中心(u)的点对数量。

    这种数点对数的问题,有一个套路。我们可以依次考虑(u)的每个儿子,看用当前的儿子,能和之前已经考虑过的所有儿子,组成多少点对。这样所有合法的点对都会被恰好计算一次。

    现在搜索(u)的一个儿子(v)的子树。对子树里的每个点,考虑它到(u)有向路径形成的串。在搜索的过程中,我们每次要在当前串的“开头”处添加一个字符(即把整个串整体右移一位),没有什么好的数据结构可以维护,于是想到哈希。现在我们要判断,当前的串,是否是“若干个(s)”的一个前缀;如果是(称这样的节点是合法的),那我们要知道它最后匹配到的“零头”是多长,也即这个“前缀”的长度(mod m)的余数是多少。具体地,在搜索时,我们维护一个桶(buc)(buc[i])表示有多少个合法的节点(x),使得(x)(u)的串的长度(mod m=i)

    这样就维护出了所有的前缀。现在我们想知道,(v)的子树内每个合法的前缀,能匹配(v)之前的子树内的多少合法的后缀。在搜索时,我们用和维护前缀类似的方法来维护后缀。对后缀,我们把(s)整体反转,然后也开一个桶,做和匹配前缀时一样的操作即可。

    同样,对于(v)子树内的所有合法的后缀,我们也要知道它能匹配(v)之前的子树内的多少合法的前缀。(这是因为路径是有向的,因此要拿(v)内的前缀匹配一次前面的后缀,再拿(v)内的后缀匹配一次前面的前缀)。

    现在完成了对(v)的子树的搜索,也把(v)子树的贡献计入了答案。我们得到了两个桶,分别是(v)内所有合法前缀的串长(mod m)的值为(i)的点的数量,和(v)内所有合法后缀的串长(mod m)的值为(i)的点的数量。现在,(v)这棵子树的身份就从“当前子树”,变成了“当前子树之前的子树”。于是拿这两个(v)的桶去分别更新两个“全局桶”即可。

    注意,桶的大小是(min(m, ext{maxdep}_v)),在更新全局桶和清空小桶时一定不能直接for(m),否则复杂度就不对了。

    除了点分治,其他部分的复杂度是线性的。因此总时间复杂度(O(nlog n))

    参考代码

  • 相关阅读:
    分页内存管理——虚拟地址到物理地址的转换【转】
    设备树(device tree)学习笔记【转】
    08 在设备树里描述platform_device【转】
    [dts]Device Tree机制【转】
    设备树快速入门【转】
    Linux 设备树详解【转】
    设备树处理之——device_node转换成platform_device【转】
    最新内核3.4)Linux 设备树加载I2C client adapter 的流程(内核3.4 高通)【转】
    基于tiny4412的Linux内核移植 --- aliases节点解析【转】
    Linux 文件系统IO性能优化【转】
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/12371944.html
Copyright © 2020-2023  润新知