【前言】
我的莫队又没排序
【题目】
BZOJ
给定一个长度为
n
n
n的字符串
S
S
S以及一个模数
P
P
P。
Q
Q
Q次询问
S
S
S的一个子串
S
[
l
…
r
]
S[l\dots r]
S[l…r]中有多少个子串表示的数字(去掉前导零后)是
P
P
P的倍数。
n
,
m
≤
1
0
5
,
P
n,m\leq 10^5,P
n,m≤105,P是一个素数。
【解题思路】
首先我们不妨求出每个后缀所代表的数字对
P
P
P取模的余数,设为
c
i
c_i
ci,对于某一段区间
[
l
,
r
]
[l,r]
[l,r],若
c
l
=
c
r
+
1
c_l=c_{r+1}
cl=cr+1,则
S
[
l
…
r
]
S[l\dots r]
S[l…r]这个字符串就是
P
P
P的倍数。为什么?根据同余的那一套理论,我们设
S
[
l
…
r
]
S_[l\dots r]
S[l…r]为
A
,
S
[
r
+
1
…
n
]
A,S_[r+1\dots n]
A,S[r+1…n]为
B
B
B则有:
A
×
1
0
n
−
r
+
1
+
B
≡
B
(mod P)
A\times 10^{n-r+1}+B\equiv B \text{ (mod P)}
A×10n−r+1+B≡B (mod P)
由于
1
0
n
−
r
+
1
̸
≡
B
(mod P)
10^{n-r+1}\not\equiv B\text{ (mod P)}
10n−r+1̸≡B (mod P),则
A
≡
0
(mod P)
A\equiv 0\text{ (mod P)}
A≡0 (mod P)
当然当模数为 2 , 5 2,5 2,5时不满足,此时用两个数组分别表示每个前缀中 2 2 2或 5 5 5的倍数时有多少种情况及有多少个数末尾是 2 2 2或 5 5 5的倍数,用前缀和维护。这部分比较简单。
当模数为其他的时候,就是区间中有多少对相同的数字,我们可以用莫队来做。
复杂度
O
(
n
n
)
O(n\sqrt n)
O(nn)
【参考代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10,lim=332;
int n,Q,mod;
char s[N];
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
void write(ll x){if(x>9)write(x/10);putchar(x%10^48);}
void writeln(ll x){write(x);putchar('\n');}
namespace S1
{
int a[N],b[N],cnt[N],bl[N];
ll res,ans[N];
struct Tquery
{
int l,r,id;
bool operator <(const Tquery&rhs)const{return bl[l]==bl[rhs.l]?r<rhs.r:l<rhs.l;}
}q[N];
void solve()
{
for(int i=n,bas=10%mod;i;--i,bas=1ll*bas*10%mod)
b[i]=a[i]=((ll)a[i+1]+1ll*(s[i]^48)*bas)%mod;
b[n+1]=0;sort(b+1,b+n+2);int cb=unique(b+1,b+n+2)-b;
for(int i=1;i<=n+1;++i) a[i]=lower_bound(b+1,b+cb+1,a[i])-b+1,bl[i]=(i-1)/lim+1;
for(int i=1;i<=Q;++i) q[i].l=read(),q[i].r=read()+1,q[i].id=i;
sort(q+1,q+Q+1);
int l=1,r=0;
for(int i=1;i<=Q;++i)
{
for(;r<q[i].r;) ++r,res+=cnt[a[r]],++cnt[a[r]];
for(;l>q[i].l;) --l,res+=cnt[a[l]],++cnt[a[l]];
for(;r>q[i].r;) --cnt[a[r]],res-=cnt[a[r]],--r;
for(;l<q[i].l;) --cnt[a[l]],res-=cnt[a[l]],++l;
ans[q[i].id]=res;
}
for(int i=1;i<=Q;++i) writeln(ans[i]);
}
}
namespace S2
{
ll cnt[N],sum[N];
void solve()
{
for(int i=1;i<=n;++i)
if(!((s[i]^48)%mod)) cnt[i]=cnt[i-1]+1,sum[i]=sum[i-1]+i;
else cnt[i]=cnt[i-1],sum[i]=sum[i-1];
for(int i=1;i<=Q;++i)
{
int l=read(),r=read();
writeln(sum[r]-sum[l-1]-(cnt[r]-cnt[l-1])*(l-1));
}
}
}
int main()
{
#ifdef Durant_Lee
freopen("BZOJ4542.in","r",stdin);
freopen("BZOJ4542.out","w",stdout);
#endif
mod=read();scanf("%s",s+1);Q=read();n=strlen(s+1);
if(mod!=2 && mod!=5) S1::solve();
else S2::solve();
return 0;
}