P6864 RC-03 记忆

P6864 RC-03 记忆

设当前括号串 \(S\) 中:

  • 合法括号非空 子串 数量为 \(\mathrm{ans}\)
  • 合法括号非空 后缀子串 数量为 \(\mathrm{cnt}\)

例: \(S = \mathtt{()()}\),则 \(\mathrm{ans} = 3\)\(\mathrm{cnt} = 2\)

\(S\) 进行一步操作一后,变为括号串 \(S_1 = \overline{S\mathtt{()}}\),相应参数变为 \(\mathrm{ans}_1\)\(\mathrm{cnt}_1\)

现试图找到 \(\mathrm{ans_1}\)\(\mathrm{ans}\)\(\mathrm{cnt_1}\)\(\mathrm{cnt}\) 的关系。

不难发现 \(\mathrm{cnt_1 = cnt + 1}\)\(\mathrm{ans_1 = ans + cnt + 1}\)

再设 \(S\) 进行一步操作二后,变为括号串 \(S_2 = \overline{\mathtt ( S\mathtt)}\),相应参数 \(\mathrm{ans_2}\)\(\mathrm{cnt_2}\)

我们试图证明,新增的合法括号非空子串只有 \(\boldsymbol{S_2}\) 本身一个。

反证法,我们考虑有没有可能,新加的这对括号中的左括号,和 \(S\) 的一段前缀组成一段合法括号子串?

很明显,这要求 \(S\) 的这段前缀中,右括号比左括号多一个。

但在操作的变换中,字符串始终是合法括号串。因此,\(S\) 不可能存在一段右括号比左括号多的前缀。

那有无可能,新加的这对括号中的右括号,和 \(S\) 的一段后缀组成一段合法括号子串?

这要求 \(S\) 的这段后缀里,左括号比右括号多一个。

可以发现,这就意味着 \(S\) 除去这段后缀的剩余那部分前缀,右括号比左括号多一个(因为整个括号串左右括号数相等),所以同样不可能。

那么新增的合法括号子串只有 \(S_2\) 本身一个咯。

所以 \(\mathrm{cnt_2} = 1\)\(\mathrm{ans_2} = \mathrm{ans} + 1\)

现在考虑撤销操作。发现撤销操作对 \(\mathrm{cnt}\)\(\mathrm{ans}\) 的变化并不平凡。

然后这里有一个比较不朴素的想法……那就是用矩阵刻画上面两种操作的变换。

刻画还是较为容易的:

\[\begin{bmatrix} \mathrm{ans} & \mathrm{cnt} & \mathrm{1} \end{bmatrix} \times \begin{bmatrix} 1 & 0 & 0\\ 1 & 1 & 0\\ 1 & 1 & 1 \end{bmatrix} = \begin{bmatrix} \mathrm{ans_1} & \mathrm{cnt_1} & \mathrm{1} \end{bmatrix} \]
\[\begin{bmatrix} \mathrm{ans} & \mathrm{cnt} & \mathrm{1} \end{bmatrix} \times \begin{bmatrix} 1 & 0 & 0\\ 0 & 0 & 0\\ 1 & 1 & 1 \end{bmatrix} = \begin{bmatrix} \mathrm{ans_2} & \mathrm{cnt_2} & \mathrm{1} \end{bmatrix} \]

那么答案可以看做初始矩阵 \(\begin{bmatrix}1 & 1 & 1\end{bmatrix}\) 经过一系列矩阵乘法得到的结果。

那撤销是什么?题目的撤销还可以撤销一个撤销操作,看起来很高级,但它的影响最后无非都是:

  • 将某个非撤销操作(即操作一或操作二)从进行改为不进行。
  • 将某个非撤销操作(即操作一或操作二)从不进行改为进行。

不进行操作也是可以刻画出矩阵的,即单位矩阵 \(\begin{bmatrix}1 & 0 & 0 \\0 & 1 & 0 \\ 0 & 0 & 1\end{bmatrix}\)

所以任何一个撤销操作都是将一个非撤销操作对应的矩阵单点修改,要么是从操作矩阵修改成单位矩阵,要么是从单位矩阵修改成操作矩阵。

因此现在变成一个对矩阵单点修改,求矩阵全局乘法的问题。因为矩阵乘法有结合律,可以用线段树维护。

因为矩阵乘法没法差分,所以不能用单 \(\log\) 的树状数组。

时间复杂度 \(\Theta(K^3 n \log n)\),其中 \(K = 3\)

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2023-05-15 16:06:19 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2023-05-15 19:15:26
 */
#include <bits/stdc++.h>
#define int long long
inline int read() {
    int x = 0;
    bool f = true;
    char ch = getchar();
    for (; !isdigit(ch); ch = getchar())
        if (ch == '-')
            f = false;
    for (; isdigit(ch); ch = getchar())
        x = (x << 1) + (x << 3) + ch - '0';
    return f ? x : (~(x - 1));
}
inline int ls(int p) {
    return p << 1;
}
inline int rs(int p) {
    return p << 1 | 1;
}

struct phalanx {
    int a[4][4];

    phalanx () {
        for (int i = 1; i <= 3; ++i)
            for (int j = 1; j <= 3; ++j)
                a[i][j] = 0;
    }
    phalanx (std :: vector <int> v) {
        for (int i = 1; i <= 3; ++i)
            for (int j = 1; j <= 3; ++j)
                a[i][j] = v[(i - 1) * 3 + j - 1];
    }

    const phalanx operator * (const phalanx b) const {
        phalanx ans;
        for (int i = 1; i <= 3; ++i)
            for (int j = 1; j <= 3; ++j)
                for (int k = 1; k <= 3; ++k)
                    ans.a[i][j] += a[i][k] * b.a[k][j];
        return ans;
    }
};

const phalanx UNIT({1, 0, 0, 0, 1, 0, 0, 0, 1}), 
                OP1({1, 0, 0, 1, 1, 0, 1, 1, 1}),
                OP2({1, 0, 0, 0, 0, 0, 1, 1, 1});

const int N = (int)2e5 + 5;

struct node {
    int l, r;
    phalanx ans;
} t[N << 2];

inline node con(node lef, node rgt) {
    return (node){
        l: lef.l,
        r: rgt.r,
        ans: lef.ans * rgt.ans
    };
}

inline void up(int p) {
    t[p] = con(t[ls(p)], t[rs(p)]);
}

void build(int p, int l, int r) {
    if (l == r) {
        t[p].l = t[p].r = l;
        t[p].ans = UNIT;
        return ;
    }
    int mid = (l + r) >> 1;
    build(ls(p), l, mid);
    build(rs(p), mid + 1, r);
    up(p);
}

void modify(int p, int x, int op) {
    int l = t[p].l, r = t[p].r;
    if (l == r) {
        if (op == 1)
            t[p].ans = OP1;
        else if (op == 2)
            t[p].ans = OP2;
        else
            t[p].ans = UNIT;
        return ;
    }
    int mid = (l + r) >> 1;
    if (x <= mid)
        modify(ls(p), x, op);
    else
        modify(rs(p), x, op);
    up(p);
}

int ops[N];
int pos[N];

signed main() {
    int n = read();
    build(1, 1, n);

    for (int i = 1; i <= n; ++i) {
        int op = ops[i] = read();
        pos[i] = i;
        if (op <= 2)
            modify(1, i, op);
        else {
            int p = read();
            p = pos[i] = pos[p];
            ops[p] = -ops[p];
            modify(1, p, ops[p]);
        }
        phalanx ans = t[1].ans;
        printf("%lld\n", ans.a[1][1] + ans.a[2][1] + ans.a[3][1]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值