注意: 所有位置从0开始。
不瞒你说我一开始以为是数学题
对于这题,我们发现原字符串是存不下的,于是它肯定与字符串算法无关。数学题
首先我们尝试维护对每一个位置为开头的答案是多少,但是我们发现直接维护不可取。于是反过来想,对每一个字符,从哪里为起点可以让这一位产生贡献(即对于一个在 (s) 中的位置 (i) ,寻找一堆 (x) 使得 (c_{x+i} ot= s_i) )。
但是对于有贡献的起点位置集,这玩意碎了一地,显然是不好维护的。于是我们按照 ((a imes x+b)\%n) 的值排序,对于样例,排序的效果如下:(接下来配合这几个串食用更佳)
012345678|位置
---------+----
627384051|值
101011010|原串
---------+----
012345678|值
000011111|新串
假设现在有一个第一位的1,我们马上就可以看出值为 (0 ext{~}3) 都是有贡献的起点位置( 值为 (0 ext{~}3) 的字符为0)。
对于第二位假设还是1,与这个1不一样的字符仍然是值为 (0 ext{~}3) 这几个位置,但问题在于我们要的是起点,而我是第二位,起点并不是我匹配的这一位,而是我在匹配 (c) 的时候的前一位。于是真正对我有贡献的起点就会发生变化。
变化就是在原串中有贡献的一堆0左移一位,这样我才能在下一位拿到一个0使我产生贡献。
那么原串左移一位对新串造成了什么影响?
还是样例:
可以注意到每一个位置到下一个位置都加了一个 (a) ,那么反过来到上一个位置就减了一个 (a) 。那么对于新串的值,也都是减一个 (a) 。由于值是连续的,减去一个 (a) 相对于向左循环移动 (a) 位,它还是一个完整的区间。
可以看出对于每一个 (s) 中字符有贡献的起点位置值都是一段完整的区间(实际做时因为会从0截断所以可能会变成两段,不过不影响,如果是环形的话就是完整的一段)。那么对于某一个位置值,(s) 中的多少个字符以这个值为起点有贡献,就是以这个值为起点的答案。注意这里的位置并不是原串 (c) 中的位置,而是 (c) 中的值。对于一个 (c) 中的位置 (x) ,它在新的排序串中的位置值为 ((a imes x+b)\%n)
于是现在问题就很简单了,对于一个1,我们对 (0 ext{~}p-1) 左移若干位后的区间加1(位移的方法是对于第 (i) 位整个区间左移 (i imes a) 位,即左右端点减去左移值)。对于一个0,我们对 (p ext{~}n) 左移后的区间加1。对于查询,我们找到查询的位置 (x) 在排序串中的位置,看看这个位置被修改了多少(对多少个 (s) 中的字符有贡献),输出即可。
就比如这样:
图真丑
那么对于第一个位置(最左边的0),这个位置被三个字符打上了有贡献的标记,于是这个值的答案就是3。
总之就是对每一个字符,把一个特定区间内的值打一个标记。对于一个值,这个值的答案就是被标记的数量。
Code
然后就是记得动态开点,可以避免离散化。
#include<cstdio>
#define mid ((l+r)>>1)
using namespace std;
struct tree{
int l,r,v;
}t[12000010];
int last,n,a,b,p,q,m;
char s[100010];
template<typename T>void read(T &x){
char c=getchar();
for(;47>c||c>58;c=getchar());
for(x=0;47<c&&c<58;x=x*10+c-48,c=getchar());
}
void read(char &x){
for(x=getchar();x<33;x=getchar());
}
void sg(int m,int x,int y,int c,int l,int r){ //区间修改
if(x<=l&&r<=y){
t[m].v+=c;
return;
}
if(x<=mid){ //左半
if(!t[m].l){
t[m].l=++last;
}
sg(t[m].l,x,y,c,l,mid);
}
if(y>mid){ //右半
if(!t[m].r){
t[m].r=++last;
}
sg(t[m].r,x,y,c,mid+1,r);
}
}
int call(int m,int x,int l,int r){ //查询
int re=t[m].v;
if(x<=mid&&t[m].l){ //左半
re+=call(t[m].l,x,l,mid);
}else if(x>mid&&t[m].r){ //右半
re+=call(t[m].r,x,mid+1,r);
}
return(re);
}
void add(int x,int y,int c){ //对某个区间进行取模,拆分
if((x=(x%n+n)%n)<(y=(y%n+n)%n)){
sg(0,x,y,c,0,n-1);
}else{
sg(0,0,y,c,0,n-1);
sg(0,x,n-1,c,0,n-1);
}
}
int main(){
read(n);read(a);read(b);read(p);read(m);
for(int i=0;i<m;i++){
read(s[i]);
if(s[i]==49){
add(-a*i,p-a*i-1,1);
}else{
add(p-a*i,n-a*i-1,1);
}
}
read(q);
for(int i=1;i<=q;i++){
char c;
int x;
read(c);read(x);
if(c=='Q'){
printf("%d
",call(0,(x*a+b)%n,0,n-1));
}else{
if(s[x]==49){
add(-a*x,p-a*x-1,-1);
add(p-a*x,n-a*x-1,1);
}else{
add(p-a*x,n-a*x-1,-1);
add(-a*x,p-a*x-1,1);
}
s[x]=97-s[x];
}
}
}