题意
分析
一开始看到的时候一脸黑人问号
注意到条件
(n,a)=1
,这意味着
∀(ai+b)%n≠(aj+b)%n
.
转换一下问题,我们用数据结构统计
s[i]
对每一个开头
c[i]
的贡献。
因为题目询问是不同的个数,所以
s[i]=1
如果有贡献,那么它一定对应
c[j]=0(j不一定是开头)
于是我们按照
f(i)=(Ai+B)%N
,将ci排好序,称为
b
。
因为
c[j]
并不是对应的开头,我们需要将贡献累加到开头
c[st]
上。
那
c[st]
映射到b上之后是否还靠在一起呢?
st=j-i (从0开始标号)
(f(j−i)+Ai)%n=f(j)
,由此可以看出,所有的合法开头在b中所对应的位置f(st)都是连续的。
f(st)是在模意义下的,所以负数区间也被允许。 同样地,大于n-1的区间也被允许。
这一题的关键之处在于把握
c[i]
在b中的映射位置
f(i)
与c[i-j]在b中的映射位置
f(i−j)
的关系,两者在c中差j,在b中差
a⋅j
。
这样我们就把所有任意s[i]需要累加贡献的位置映射到一段区间内,使用动态开点+权值线段树保存修改即可。
Code
#include <iostream>
#include <cstdio>
#include <bitset>
#define N 101000
#define SZ 12000000
#define f(i) (((i)*a+b)%n)
#define tag(x,y) ((x)!=0?lazy[x]+=(y):0)
using namespace std;
int s[N];
int n,a,b,p,m,q;
int lc[SZ],rc[SZ],tot,lazy[SZ],root;
void down(int x) {
if (lazy[x]) tag(lc[x],lazy[x]),tag(rc[x],lazy[x]),lazy[x]=0;
}
void query(int x,int l,int r,int tg,int &ans) {
if (x==0) return;
if (l>tg || r<tg) return;
if (l==r) {
ans=lazy[x]; return;
}
if (!lc[x]) lc[x]=++tot;
if (!rc[x]) rc[x]=++tot;
down(x);
query(lc[x],l,l+r>>1,tg,ans);
query(rc[x],(l+r>>1)+1,r,tg,ans);
}
void change(int &x,int l,int r,int tl,int tr,int v) {
if (tr<l || tl>r) return;
if (x==0) x=++tot;
if (tl<=l && r<=tr) {tag(x,v); return;}
change(lc[x],l,l+r>>1,tl,tr,v);
change(rc[x],(l+r>>1)+1,r,tl,tr,v);
}
void add(int x,int y,int v) {
x=(x%n+n)%n,y=(y%n+n)%n;
if (x<y) change(root,0,n-1,x,y,v);
else {
change(root,0,n-1,0,y,v);
change(root,0,n-1,x,n-1,v);
}
}
int main() {
freopen("3.in","r",stdin); freopen("3.out","w",stdout);
cin>>n>>a>>b>>p>>m; scanf("\n");
char cc;
for (int i=0; i<m; i++) {
scanf("%c",&cc),s[i]=cc-'0';
if (s[i]==1) add(0-a*i,p-1-a*i,1);
else add(p-a*i,n-1-a*i,1);
}
cin>>q;int cnt=0;
for (int i=1; i<=q; i++) {
scanf("\n"); cc=getchar();
if (cc=='Q') {
int st,ans=0; scanf("uery %d",&st);
query(1,0,n-1,f(st),ans);
printf("%d\n",ans);
} else {
int t; scanf("hange %d",&t);
if (s[t]==0) {
add(p-a*t,n-1-a*t,-1);
add(0-a*t,p-a*t-1,1);
} else {
add(p-a*t,n-1-a*t,1);
add(0-a*t,p-a*t-1,-1);
}
s[t]^=1;
}
}
}