JZOJ4417 【HNOI2016模拟4.1】神奇的字符串 线段树维护信息

本文介绍了一种处理大规模循环字符串查询与更新问题的有效算法。通过巧妙地将问题转化为基于(A*i+B)modN的分类处理,利用线段树结构进行区间覆盖计数,实现了快速查询指定位置的字符差异及字符串的局部反转操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意

给定5个参数,分别为N,A,B,P,M其中我们可以生成字符串c中的第ici=[(Ai+B)modNB],下标为0~N1,并且这个字符串是循环的。现在给你另一个M为的字符串S,现在有Q组操作,有两种:每种都给定一个p
1. 询问c的第p位开始往取M位得到的字符串与S有多少位不同。
2. 将S的第p为取反。

N109
1A,B,P,MN
1N,Q105

解题思路

如果我们直接拿生成的字符串去做,那么这个是很难维护了,那么我们考虑把(Ai+B)modN相同的分成一类。我们考虑c中的哪些字符串的第i位于Si不相等。根据上面那条式子,移一下项。如果我们把按(Ai+B)modN为下标的话,那么对应的位置肯定是连续一段的,那么我们就可以用线段树来维护一段区间覆盖的点。最后的答案就是(Acp+B)modN被覆盖了多少次。

程序

//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

const int MAXN = 1e5 + 5;

struct Node {
    int l, r, Num;
} Tr[MAXN * 50];

int tot, Q, N, A, B, P, M, Root;
char S[MAXN], C[10];

int Calc(int i) {
    return (1ll * i * A + B) % N;
}

int Query(int Now, int l, int r, int Side) {
    if (!Now) return 0;
    if (l == r) return Tr[Now].Num;
    int Mid = (l + r) >> 1;
    if (Side <= Mid) return Tr[Now].Num + Query(Tr[Now].l, l, Mid, Side); else
        return Tr[Now].Num + Query(Tr[Now].r, Mid + 1, r, Side);
}

void Modify(int &Now, int l, int r, int lx, int rx, int val) {
    if (rx < l || lx > r) return;
    if (!Now) Now = ++ tot;
    if (l >= lx && r <= rx) {
        Tr[Now].Num += val;
        return;
    }
    int Mid = (l + r) >> 1;
    Modify(Tr[Now].l, l, Mid, lx, rx, val), Modify(Tr[Now].r, Mid + 1, r, lx, rx, val);
}

void Modify(int v, int val) {
    int c = S[v] - '0', l, r;
    if (c == 0) l = 0, r = P - 1; else l = P, r = N - 1;
    l = ((l % N - 1ll * A * v % N) % N + N) % N;
    r = ((r % N - 1ll * A * v % N) % N + N) % N;
    if (l <= r) Modify(Root, 0, N - 1, l, r, val); else
        Modify(Root, 0, N - 1, l, N - 1, val), Modify(Root, 0, N - 1, 0, r, val); 
} 

int main() {
    scanf("%d%d%d%d%d\n", &N, &A, &B, &P, &M);
    scanf("%s", S);
    Root = tot = 1;
    for (int i = 0; i < M; i ++) Modify(i, 1);
    scanf("%d", &Q);
    for (int i = 1; i <= Q; i ++) {
        int v;
        scanf("\n%s%d", C + 1, &v);
        if (C[1] == 'Q') printf("%d\n", M - Query(Root, 0, N - 1, Calc(v))); else {
            Modify(v, -1);
            S[v] = (S[v] == '0') ? '1' : '0';
            Modify(v, 1);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值