Alice and Bod

Alice and Bod

Alice 和 Bob 正在玩一个最近兴起的游戏,名为死亡对称,一人进攻一人防守,进攻方可以进攻 m 次,m 次后如果防守成功则防守方获胜,否则进攻方获胜。

Alice 防守,Bob 进攻。 具体规则如下,给定一个长度为 n 的字符串,进攻方 Bob 每次可以进行如下两种操作的一种。

1 l r 向 Alice 询问一段字符串的子串 s[l,r] 是否是对称的,即要求 sl+i=sri(0irl)

2 l r x 将给定字符串的子串 s[l,r] 一段的每个字符同时变成它在字典序中后面的第 x(0x109) 个字符,并且规定 z 在字典序中后面的第 1 个字符为 a

Alice 很想赢得这个游戏,但是他对于字符串一窍不通,希望得到你的帮助,在每次询问时做出正确的答案。

输入描述:

第一行输入两个整数 n(1n105)m(1m105) 分别表示字符串的长度和进攻次数。

第二行输入一个长度为 n 的字符串 s,保证全部由小写字母构成。 接下来输入 m 行,每行表示一个操作。

输出描述:

对于每次 Bob 的第一种进攻,若是对称的则输出 YES ,否则输出 NO。

示例1

输入

3 5
aaz
1 1 3
2 3 3 1
1 1 2
2 1 3 1
1 1 3

输出

NO
YES
YES

说明

第一次操作时,字符串为 aaz ,子串 s[1...3],为 aaz,并不对称,输出 NO。

第二次操作,字符串变为 aaa 第三次操作时,字符串为 aaa,子串 s[1...2]aa,对称,输出 YES。

第四次操作,字符串变为 bbb 第五次操作,字符串为 bbb,子串 s[1...3],为 bbb,是对称的,输出 YES。

 

解题思路

  要判断子串 slsr 是不是回文串,一种 O(1) 的判断方法是判断 h(slsr) 是否等于 h(srsl),其中 h(s) 是字符串 s 的哈希值。因此我们可以用线段树去维护每个区间对应的正反子串的哈希值。具体的,对于维护区间 [l,r] 的线段树节点,维护正子串 slsr 的哈希值 h1=slPrl+sl+1Prl1++srP0,维护反子串 srsl 的哈希值 h2=slP0+sl+1P1++srPrl。假设要与另外一个字符串 s 拼接,那么拼接后的字符串的哈希值分别是 h1P|s|+h1h2+h2P|s|,这对应 pushup 操作。

  以 h1 为例,对于修改操作,需要将其更新为

((sl+x)mod26)Prl+((sl+1+x)mod26)Prl1++((sr+x)mod26)P0

  可以发现很难仅通过原本的 h1 去更新成这个值。对于修改操作,本质上是将每种字符都循环右移 x 位。

  举个具体的例子,有 s=caazcax=2,原本的 h1=cP5+aP4+aP3+zP2+cP1+aP0。修改后字符串变成 s=eccbec,对应的 h1=eP5+cP4+cP3+bP2+eP1+cP0。可以发现,原本的 aP4+aP3+aP0 变成 cP4+cP3+cP0;原本的 cP5+cP1 变成 eP5+eP1;原本的 zP2 变成 bP2

  这启发我们可以开个大小为 26 的数组来存储每个字符对应的多项式,修改的时候只需对该数组循环右移 x 个单位,就可以得到每种字符变化后得到的多项式,而 h1 则可以通过对各个字符乘以其多项式求和得到。对 h2 的维护同理。更多的细节可以参考代码。

  自然溢出取模的 AC 代码如下,时间复杂度为 O(26mlogn)

#include <bits/stdc++.h>
using namespace std;

typedef unsigned long long ULL;

const int N = 1e5 + 5, P = 13331;

char s[N];
ULL p[N];
struct Node {
    int l, r, len, add;
    array<ULL, 26> h1, h2;
}tr[N * 4];

void pushup(Node &u, Node l, Node r) {
    u.len = l.len + r.len;
    for (int i = 0; i < 26; i++) {
        u.h1[i] = l.h1[i] * p[r.len] + r.h1[i];
        u.h2[i] = l.h2[i] + r.h2[i] * p[l.len];
    }
}

void build(int u, int l, int r) {
    tr[u] = {l, r, r - l + 1};
    if (l == r) {
        int t = s[l] - 'a';
        tr[u].h1[t] = tr[u].h2[t] = 1;
    }
    else {
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
    }
}

void upd(int u, int c) {
    rotate(tr[u].h1.begin(), tr[u].h1.end() - c, tr[u].h1.end());
    rotate(tr[u].h2.begin(), tr[u].h2.end() - c, tr[u].h2.end());
    tr[u].add = (tr[u].add + c) % 26;
}

void pushdown(int u) {
    if (tr[u].add) {
        upd(u << 1, tr[u].add);
        upd(u << 1 | 1, tr[u].add);
        tr[u].add = 0;
    }
}

void modify(int u, int l, int r, int c) {
    if (tr[u].l >= l && tr[u].r <= r) {
        upd(u, c);
    }
    else {
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) modify(u << 1, l, r, c);
        if (r >= mid + 1) modify(u << 1 | 1, l, r, c);
        pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
    }
}

Node query(int u, int l, int r) {
    if (tr[u].l >= l && tr[u].r <= r) return tr[u];
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    if (r <= mid) return query(u << 1, l, r);
    if (l >= mid + 1) return query(u << 1 | 1, l, r);
    Node t;
    pushup(t, query(u << 1, l, r), query(u << 1 | 1, l, r));
    return t;
}

int main() {
    int n, m;
    cin >> n >> m >> s;
    memmove(s + 1, s, n + 1);
    p[0] = 1;
    for (int i = 1; i <= n; i++) {
        p[i] = p[i - 1] * P;
    }
    build(1, 1, n);
    while (m--) {
        int op, l, r;
        cin >> op >> l >> r;
        if (op == 1) {
            Node t = query(1, l, r);
            ULL h = 0;
            for (int i = 0; i < 26; i++) {
                h += (t.h1[i] - t.h2[i]) * (i + 1);
            }
            cout << (h ? "NO" : "YES") << '\n';
        }
        else {
            int c;
            cin >> c;
            modify(1, l, r, c % 26);
        }
    }
    
    return 0;
}

  随机模数的 AC 代码如下,时间复杂度为 O(26mlogn)

#include <bits/stdc++.h>
using namespace std;

typedef unsigned long long LL;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

const int N = 1e5 + 5;
const LL P = uniform_int_distribution<int> (131, 13331)(rng), mod = uniform_int_distribution<int> (1e9, 2e9)(rng);

char s[N];
LL p[N];
struct Node {
    int l, r, len, add;
    array<int, 26> h1, h2;
}tr[N * 4];

void pushup(Node &u, Node l, Node r) {
    u.len = l.len + r.len;
    for (int i = 0; i < 26; i++) {
        u.h1[i] = (l.h1[i] * p[r.len] + r.h1[i]) % mod;
        u.h2[i] = (l.h2[i] + r.h2[i] * p[l.len]) % mod;
    }
}

void build(int u, int l, int r) {
    tr[u] = {l, r, r - l + 1};
    if (l == r) {
        int t = s[l] - 'a';
        tr[u].h1[t] = tr[u].h2[t] = 1;
    }
    else {
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
    }
}

void upd(int u, int c) {
    rotate(tr[u].h1.begin(), tr[u].h1.end() - c, tr[u].h1.end());
    rotate(tr[u].h2.begin(), tr[u].h2.end() - c, tr[u].h2.end());
    tr[u].add = (tr[u].add + c) % 26;
}

void pushdown(int u) {
    if (tr[u].add) {
        upd(u << 1, tr[u].add);
        upd(u << 1 | 1, tr[u].add);
        tr[u].add = 0;
    }
}

void modify(int u, int l, int r, int c) {
    if (tr[u].l >= l && tr[u].r <= r) {
        upd(u, c);
    }
    else {
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) modify(u << 1, l, r, c);
        if (r >= mid + 1) modify(u << 1 | 1, l, r, c);
        pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
    }
}

Node query(int u, int l, int r) {
    if (tr[u].l >= l && tr[u].r <= r) return tr[u];
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    if (r <= mid) return query(u << 1, l, r);
    if (l >= mid + 1) return query(u << 1 | 1, l, r);
    Node t;
    pushup(t, query(u << 1, l, r), query(u << 1 | 1, l, r));
    return t;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m >> s;
    memmove(s + 1, s, n + 1);
    p[0] = 1;
    for (int i = 1; i <= n; i++) {
        p[i] = p[i - 1] * P % mod;
    }
    build(1, 1, n);
    while (m--) {
        int op, l, r;
        cin >> op >> l >> r;
        if (op == 1) {
            Node t = query(1, l, r);
            int h = 0;
            for (int i = 0; i < 26; i++) {
                h = (h + (t.h1[i] - t.h2[i]) * (i + 1ll)) % mod;
            }
            cout << (h ? "NO" : "YES") << '\n';
        }
        else {
            int c;
            cin >> c;
            modify(1, l, r, c % 26);
        }
    }
    
    return 0;
}

 

参考资料

  题解 | #练习赛129题解#:https://blog.nowcoder.net/n/dab6e8baab16457da0e5ac1c72da2d9d

posted @   onlyblues  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示