首页 > 其他分享 >Alice and Bod

Alice and Bod

时间:2024-09-29 23:12:27浏览次数:6  
标签:26 cdot h1 tr Alice len int Bod

Alice and Bod

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

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

1 l r 向 Alice 询问一段字符串的子串 $s[l,r]$ 是否是对称的,即要求 $s_{l+i}=s_{r−i}​(0 \leq i \leq r−l)$。

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

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

输入描述:

第一行输入两个整数 $n(1 \leq n \leq 10^5)$,$m(1 \leq m \leq 10^5)$ 分别表示字符串的长度和进攻次数。

第二行输入一个长度为 $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。

 

解题思路

  要判断子串 $s_l \ldots s_r$ 是不是回文串,一种 $O(1)$ 的判断方法是判断 $h(s_l \ldots s_r)$ 是否等于 $h(s_r \ldots s_l)$,其中 $h(s)$ 是字符串 $s$ 的哈希值。因此我们可以用线段树去维护每个区间对应的正反子串的哈希值。具体的,对于维护区间 $[l,r]$ 的线段树节点,维护正子串 $s_l \ldots s_r$ 的哈希值 $h_1 = s_{l} \cdot P^{r-l} + s_{l+1} \cdot P^{r-l-1} + \cdots + s_{r} \cdot P^{0}$,维护反子串 $s_r \ldots s_l$ 的哈希值 $h_2 = s_{l} \cdot P^{0} + s_{l+1} \cdot P^{1} + \cdots + s_{r} \cdot P^{r-l}$。假设要与另外一个字符串 $s'$ 拼接,那么拼接后的字符串的哈希值分别是 $h_1 \cdot P^{|s'|} + h_1'$,$h_2 + h_2' \cdot P^{|s|}$,这对应 pushup 操作。

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

$$((s_{l} + x) \bmod 26) \cdot P^{r-l} + ((s_{l+1} + x) \bmod 26) \cdot P^{r-l-1} + \cdots + ((s_{r} + x) \bmod 26) \cdot P^{0}$$

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

  举个具体的例子,有 $s = caazca$,$x = 2$,原本的 $h_1 = c \cdot P^{5} + a \cdot P^{4} + a \cdot P^{3} + z \cdot P^{2} + c \cdot P^{1} + a \cdot P^{0}$。修改后字符串变成 $s' = eccbec$,对应的 $h_1' = e \cdot P^{5} + c \cdot P^{4} + c \cdot P^{3} + b \cdot P^{2} + e \cdot P^{1} + c \cdot P^{0}$。可以发现,原本的 $a \cdot P^{4} + a \cdot P^{3} + a \cdot P^{0}$ 变成 $c \cdot P^{4} + c \cdot P^{3} + c \cdot P^{0}$;原本的 $c \cdot P^{5} + c \cdot P^{1}$ 变成 $e \cdot P^{5} + e \cdot P^{1}$;原本的 $z \cdot P^{2}$ 变成 $b \cdot P^{2}$。

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

  自然溢出取模的 AC 代码如下,时间复杂度为 $O(26 \, m \log{n})$:

#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(26 \, m \log{n})$:

#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

标签:26,cdot,h1,tr,Alice,len,int,Bod
From: https://www.cnblogs.com/onlyblues/p/18440933

相关文章

  • CF1592F2-Alice and Recoloring 2-分析、二分图
    link:https://codeforces.com/contest/1592/problem/F2题意:给定一个\(n\)行\(m\)列的目标矩阵,矩阵元素只有W或B,并且你有一个初始矩阵,元素全为W。现在你可以矩阵实施以下操作:使用一块钱,选定一个包含\((1,1)\)的子矩阵,把矩阵中的元素全部反转(W变B,B变W)。使用......
  • NoBodyPro AI短剧革新未来
        在数字时代,AI技术的飞速发展正引领着各行各业进行深刻的变革。而今天,我们要介绍的是一个令人兴奋的项目——NoBodyPro,它巧妙地将AI技术融入短剧社交领域,打造了一个前所未有的互动体验平台。一、AI赋能短剧社交:NoBodyPro的创新之路    NoBodyPro,作为一个新型的A......
  • post请求的body数据类型和content-type的关系
    背景:登陆接口的类型是post,request接口的content-type是multipart/form-data;boundary=----WebKitFormBoundaryxeYAwSy6FSo4kow9response接口的content-type是application/json;charset=utf-8接口的请求体在编写python脚本时post接口的请求头content-type定义了类型multipar......
  • 配置alice
    配置alicehttps://github.com/alist-org/alist/release#下载alist压缩包alist-windows-amd64.zip下载自己对应的版本windows64版本#下载aria2chttps://github.com/aria2/aria2/releases右键点击“此电脑”,选择“属性”。点击“高级系统设置”,然后点击“环境变量”。......
  • HTML讲解(一)body部分
    目录1.什么是HTML 2.HTML基本框架3.标题声明4.修改标题位置5.段落声明6.修改段落位置7.超链接访问8.图像访问9.改变网页背景及文本颜色10.添加网页背景图11.超链接改变颜色12.设置网页边距小心!VS2022不可直接接触,否则!没这个必要,方源面色淡然一把抓住!顷刻炼化!......
  • P11072 Alice and Bob 题解
    简单博弈题。先说结论,如果存在\(a_i=0\)使得\(1\lei\lea_1\)的话,那么先手必胜,否则后手必胜。若满足上述条件显然先手必胜,将\(0\)搞到第一个就行。否则Alice每操作一次,如果操作后满足了上述条件,那么Bob赢,否则Bob只要不动就行。但是下一轮Alice必须动,要不然两......
  • 轻松构建RESTful API:Spring @ResponseBody注解全攻略,有两下子!
    ......
  • Alice和Bob的爱恨情仇(lanqiao OJ 3865)
    问题如下(附链接):Alice和Bob的爱恨情仇题解代码如下:#include<bits/stdc++.h>usingnamespacestd;intmain(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);intn,k;cin>>n>>k;intans=0;for(inti=0;i<n;i++){intx;cin>>......
  • [Embodied AI Tutorial] The Basic Frameworks and Techniques for Embodied AI (Part
    目录EmbodiedAITutorial课程内容ModelingandapproachesforEmbodiedAIWorldModelGetaGoodPolicyPlanningAndControlSimulationtechnologyforEmbodiedAIRigidbodysimulationCamerasimulationAsserts相关链接资料查询EmbodiedAITutorial课程主页:slidesvide......
  • [Embodied AI Tutorial] Overview of Embodied AI (Part1)
    OverviewofEmbodiedAI(Part1)课程主页:https://ai-workshops.github.io/building-and-working-in-environments-for-embodied-ai-cvpr-2022/slidesvideo讲师:ZhiweiJia课程内容Simulators使用仿真引擎主要考虑如下因素:Rendering:RGB/Depth/OpticalFlow/Segmentatio......