zoukankan      html  css  js  c++  java
  • LOJ#6074 「2017 山东一轮集训 Day6」

    LOJ#6074 「2017 山东一轮集训 Day6」

    转自 https://www.dazhuanlan.com/2019/12/10/5deec6dec86e4/

    原文的 markdown 都挂了,这里修修

    设字符集大小为 m,这里 (m=9),假设字符从 (0) 开始标号。

    显然可以 DP,设 (dp_{i,j}) 表示前 (i) 个字符中,以字符 (j) 结尾的本质不同的子序列个数。特殊地,(j=m) 表示子序列为空。有方程:

    [dp_{i,j}=egin{cases}dp_{i-1,j} ext{ if } j e S_i \sum_{k=0}^{m} dp_{i-1,k} ext{ if }j=S_i end{cases} ]

    初始值是 (dp_{0,m}=1)。 发现 dp 数组可以写成行向量,记 (F_i=egin{pmatrix}dp_{i,0}cdots dp_{i,1} cdots dp_{i,2} cdots dp_{i,m}end{pmatrix})

    而转移可以表示为一个行向量乘上转移矩阵 (M_i) 的形式:

    [egin{aligned}M_i = egin{pmatrix}1 & & 1 & & \ & 1 & 1 & & \ & & 1 & & \ & & vdots & ddots & \ & & 1 & & 1 end{pmatrix} \\ F_i &= F_{i-1}M_i end{aligned} ]

    (M_i) 是一个主对角线为 (1),(S_i) 这一列为 (1),其余为 (0) 的矩阵。

    初始的行向量为 (A=egin{pmatrix}0 & 0 & 0 & cdots & 0 & 1end{pmatrix})

    最后由于是求 $$ left(sum_{i=0}^m dp_{n,i} ight)-1 $$ 所以最后需要乘上列向量

    [B=egin{pmatrix}1 \ 1 \ 1 \ vdots \ 1 end{pmatrix} ]

    于是对于每个询问,答案可以表示成

    [egin{aligned}ans&=AM_lM_{l+1}M_{l+2}cdots M_{r}B\&=AM_{l-1}^{-1}M_{l-2}^{-1}cdots M_1^{-1}M_1M_2cdots M_{r}Bend{aligned} ]

    直接维护 (M_i)(M_i^{-1}) 的前缀积(注意顺序),时间复杂度 (O(nm^3+qm^3))

    在维护完 (M_i)(M_i^{-1}) 后,分别右乘一个 B 和左乘一个 A,这样询问部分的复杂度可以降到 (O(qm))

    预处理部分,我们来观察一些特殊性质。对于 (M_i) 的前缀积,我们发现每次要算

    [egin{aligned}CM_i&=egin{pmatrix}C_{0,0} & C_{0,1} & C_{0,2} & cdots & C_{0,m} \ C_{1,0} & C_{1,1} & C_{1,2} & cdots & C_{1,m} \ C_{2,0} & C_{2,1} & C_{2,2} & cdots & C_{2,m} \ & & & ddots & \ C_{m,0} & C_{m,1} & C_{m,2} & cdots & C_{m,m} end{pmatrix}egin{pmatrix}1 & & 1 & & \ & 1 & 1 & & \ & & 1 & & \ & & vdots & ddots & \ & & 1 & & 1 end{pmatrix} \ &= egin{pmatrix}C_{0,0} & C_{0,1} & sum_{j=0}^m C_{0,j} & cdots & C_{0,m} \ C_{1,0} & C_{1,1} & sum_{j=0}^m C_{1,j} & cdots & C_{1,m} \ C_{2,0} & C_{2,1} & sum_{j=0}^m C_{2,j} & cdots & C_{2,m} \ & & & ddots & \ C_{m,0} & C_{m,1} & sum_{j=0}^m C_{m,j} & cdots & C_{m,m} end{pmatrix}end{aligned} ]

    相当于把 (C) 的第 (S_i) 列改成对应行的 (C_{k,j}) 之和。且最后乘上 (B) 相当于求每行的和。那么只要维护 (C) 的每一行之和即可。于是维护 (M_i) 的前缀积乘上 (B) 的复杂度变成了 (O(nm))

    对于 (M_i^{-1}) 的前缀积,我们先根据矩阵求逆的过程手动求出 (M_i^{-1}),发现

    [M_i^{-1}=egin{pmatrix}1 & & -1 & & \ & 1 & -1 & & \ & & 1 & & \ & & vdots & ddots & \ & & -1 & & 1 end{pmatrix} ]

    (M_i^{-1}) 是主对角线为 (1),第 (S_i) 列除了第 (S_i) 行外是 (-1) 的矩阵。

    维护 (M_i^{-1}) 的前缀积时,我们要算

    [egin{aligned}M_i^{-1}C&=egin{pmatrix}1 & & -1 & & \ & 1 & -1 & & \ & & 1 & & \ & & vdots & ddots & \ & & -1 & & 1 end{pmatrix}egin{pmatrix}C_{0,0} & C_{0,1} & C_{0,2} & cdots & C_{0,m} \ C_{1,0} & C_{1,1} & C_{1,2} & cdots & C_{1,m} \ C_{2,0} & C_{2,1} & C_{2,2} & cdots & C_{2,m} \ & & & ddots & \ C_{m,0} & C_{m,1} & C_{m,2} & cdots & C_{m,m} end{pmatrix} \ &= egin{pmatrix}C_{0,0}-C_{2,0} & C_{0,1}-C_{2,1} & C_{0,2}-C_{2,2} & cdots & C_{0,m}-C_{2,m} \ C_{1,0}-C_{2,0} & C_{1,1}-C_{2,1} & C_{1,2}-C_{2,2} & cdots & C_{1,m}-C_{2,m} \ C_{2,0} & C_{2,1} & C_{2,2} & cdots & C_{2,m} \ vdots & vdots & vdots & ddots & vdots \ C_{m,0}-C_{2,0} & C_{m,1}-C_{2,1} & C_{m,2}-C_{2,2} & cdots & C_{m,m}-C_{2,m} end{pmatrix}end{aligned} ]

    可以发现,每一列除了 (S_i) 这一行以外都减去的是同一个数,于是我们维护每一列减去的数,例如对于某一列

    [egin{pmatrix}x_0-v \ x_1-v \ x_2-v \ vdots \ x_m-v end{pmatrix} ]

    要变成

    [egin{pmatrix}x_0-v-(x_2-v) \ x_1-v-(x_2-v) \ x_2-v \ vdots \ x_m-v-(x_2-v) end{pmatrix}=egin{pmatrix}x_0-x_2 \ x_1-x_2 \ (2x_2-v)-x_2 \ vdots \ x_m-x_2 end{pmatrix}$​ ]

    于是只需要修改第 (S_i) 那一行的值,并且更新每一列减去的值即可。

    最后左乘 (A) 相当于求最后一行的值。由于最后一行不会修改,始终是

    (egin{pmatrix}0 & 0 & 0 & cdots & 0 & 1 end{pmatrix})

    所以直接减去对应列需要减的数即可。这一部分的时间复杂度也变成了 (O(nm))

    总时间复杂度 (O(nm+qm))

  • 相关阅读:
    GNOME3启动时出错:Oh no! Something has gone wrong.Logout!
    设计模式之迭代器模式
    C#中多线程 委托的使用
    python的memcache使用如果对key设置了一个int型
    Linux select TCP并发服务器与客户端编程
    G++ 教程(转)
    汉语-词语:卓识
    汉语-词语:决断
    汉语-词语:远见
    汉语-词语:胆识
  • 原文地址:https://www.cnblogs.com/Hs-black/p/14122842.html
Copyright © 2011-2022 走看看