zoukankan      html  css  js  c++  java
  • 【UOJ46】玄学

    这道题是一个月之前就应该会的了,但是一直没有弄懂,今天回放了一遍,把它弄懂了。

    题意简述:

    一个操作是把位于$[l..r]$的所有元素$x$变换成$(ax+b)\%m$。

    强制在线,每次定义一个新的操作,或是询问只作用变换$l..r$则位于$k$位置的元素应该变成多少。

    题解:

    这道题很神仙,应用的新的思路。

    题解上说是二进制分组,但其实里面只开了线段树,就是这个地方没有弄懂。

    我们不妨分析一下这个操作,考虑一下它的性质吧。

    先分析有没有交换律,发现显然是没有的。

    但是我们惊喜的发现这个操作有结合律,所以或许可以用线段树维护,一个节点的操作可以看成左右儿子叠加形成。

    进一步的,我们能否对操作求逆,即如果我们知道了操作$[1..l-1]$、$[1..r]$后的结果,能不能推出$[l..r]$的操作。

    发现这样是不行的,假设$ax+b$经过操作$cx+d$之后变成了$ex+f$,当$p$不为质数时,方程$ac ≡ e (mod p)$无唯一的解。

    我们考虑两种形式的线段树分治。

    一种是线段树对位置开,对于一个修改操作,我们确定$log$个区间,节点内部因为已经考虑了位置,就按时间排序,建线段树,查询的时候一条链查下去就可以了。

    但是发现这样做很$fake$,因为这个操作是没有交换律的(对于论文上可以说是修改贡献不独立)所以肯定不能这样做。

    一种是线段树对操作序列开,也就是题解的做法,但遗憾的是,我依旧没有想出来是怎么做的。

    说说我的做法困难在哪里,

    刚刚已经说了操作有结合律,所以对于查询的时候,我们可以在线段树上确定$log$个区间,然后依次进行合并。

    但是对于我们节点内部,仿佛除了问题规模缩小以外,没有其他的突破,依旧需要按照时间的顺序把所有覆盖了$loc$位置的区间依次合并,这在我看来是一个棘手的二维问题($APIO2019T3$),是不能得到好的解决方法的。

    不破不立。

    借鉴一般的启发式合并的方法,线段树的每个节点维护这些操作完成后,序列长什么样,也就是一个$vector$,一个元素代表一段区间。

    但是这样做$pushup$的复杂度就和左右儿子的区间个数有关了,所以肯定不能像普通的线段树那样,修改一点就对一条链从下到上$pushup$。

    因为这样修改是从左到右进行的,所以当还没有到$x$时,线段树上所有覆盖了$x$的区间都是无需维护的,从位置上考虑,我们仅需要对左边的祖先$pushup$就可以了。

    $Gloid$爷说这样做是两个$log$的,让我来分析一下。

    线段树上有$2n$个节点,平摊到每一个位置就是$O(1)$的,而一次$pushup$复杂度是左右儿子区间的个数之和,一个点最多$pushup$一下,所以修改总的是一个$log$的,查询需要二分,是两个$log$的(但是常数极小)。

    神奇啊,被认为是瓶颈的修改竟然没有背这个黑锅。

    这个解决的方法可以扩展到一般的二进制分组问题(要求访问历史状态的修改,乃至对一段区间的修改应用),要求是修改具有结合律。

    但是大部分修改都不具有结合律,如我们不能快速合并两个凸包,也不能快速合并两个$AC$自动机,可能这个做法的用处是比较窄的,多数二进制分组的题都是把组拆掉重建的。

  • 相关阅读:
    as3 的相关资源
    linux 进程用户栈和内核栈
    Chapter 11 进程与信号 @ linux
    linux/unix下setuid/seteuid/setreuid/setresuid
    poj 3259 spfa 虫洞问题判到点1时候有环
    My Vimrc Archive
    C/C++函数调用的几种方式
    Git常用命令解说 [robby certification]
    Linux Chapter 11 进程与信号
    XNA游戏开发之(四)——改变Draw频率
  • 原文地址:https://www.cnblogs.com/shxnb666/p/11628644.html
Copyright © 2011-2022 走看看