题目描述
小 B 有一个很大的数 S,长度达到了 N 位;这个数可以看成是一个串,它可能有前导 0,例如00009312345。小B还有一个素数P。现在,小 B 提出了 M 个询问,每个询问求 S 的一个子串中有多少子串是 P 的倍数(0 也是P 的倍数)。例如 S为0077时,其子串 007有6个子串:0,0,7,00,07,007;显然0077的子串007有6个子串都是素数7的倍数。
输入输出格式
输入格式:
第一行一个整数:P。第二行一个串:S。第三行一个整数:M。接下来M行,每行两个整数 fr,to,表示对S 的子串S[fr…to]的一次询问。注意:S的最左端的数字的位置序号为 1;例如S为213567,则S[1]为 2,S[1…3]为 213。N,M<=100000,P为素数
输出格式:
输出M行,每行一个整数,第 i行是第 i个询问的答案。
思路
之前做过的题,今天在看到还是不会QAQ。痛下决心要把它搞懂。
定义a数组是字符串的*后缀和(类似哈希)。
对于P的倍数,可以得到等式:
a [ L ] − a [ R + 1 ] ∗ 1 0 R − L + 1 ≡ 0 ( m o d P ) a[L]-a[R+1]*10^{R-L+1}\equiv0\ \ (mod\ P) a[L]−a[R+1]∗10R−L+1≡0 (mod P)
即
a
[
L
]
≡
a
[
R
+
1
]
∗
1
0
R
−
L
+
1
(
m
o
d
P
)
a[L]\equiv a[R+1]*10^{R-L+1}\ \ (mod \ P)
a[L]≡a[R+1]∗10R−L+1 (mod P)
当 P ≠ 2 P\neq 2 P̸=2且 P ≠ 5 P\neq 5 P̸=5时, 1 0 L 10^{L} 10L有乘法逆元,得到
a [ L ] ∗ 1 0 L ≡ a [ R + 1 ] ∗ 1 0 R + 1 a[L]*10^{L}\equiv a[R+1]*10^{R+1} a[L]∗10L≡a[R+1]∗10R+1
然后题目就变成求数组中相同数字的个数,用莫队搞定。 P = 2 P=2 P=2或 P = 5 P=5 P=5时特判一下就好了。
代码
#include<map>
#include<cmath>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
const int maxn=5e5+5;
char s[maxn];
std :: map <long long,int> mp;
int P,N,Q,L,R,cnt,siz,blk[maxn],V[maxn];
long long ans,bas,Ans[maxn],sum[maxn],hsh[maxn];
struct node {int L,R,id;} a[maxn];
inline bool operator <(node a,node b) {
return blk[a.L]^blk[b.L]?a.L<b.L:(blk[a.L]&1?a.R<b.R:a.R>b.R);
//学长教的莫队小优化,无法优化复杂度,但效果立竿见影
}
inline int read() {
int ret=0,f=1,ch=getchar();
for (; !isdigit(ch); ch=getchar()) if (ch=='-') f=-f;
for (; isdigit(ch); ch=getchar()) ret=ret*10+ch-48;
return ret*f;
}
inline void revise(int pos,int add) {
ans-=hsh[V[pos]]*(hsh[V[pos]]-1)/2;
hsh[V[pos]]+=add;
ans+=hsh[V[pos]]*(hsh[V[pos]]-1)/2;
}
inline void Solve() {
for (int i=1; i<=N; ++i)
Ans[i]=Ans[i-1]+((s[i]-48)%P==0)*i,sum[i]=sum[i-1]+((s[i]-48)%P==0);
for (int Q=read(),L,R; Q--; )
L=read(),R=read(),printf("%d\n",Ans[R]-Ans[L-1]-(sum[R]-sum[L-1])*(L-1));
}
int main() {
register int i;
scanf("%d\n%s",&P,s+1),N=strlen(s+1),siz=sqrt(N);
if (P==2||P==5) return Solve(),0;
for (i=1; i<=N+1; ++i) blk[i]=(i-1)/siz+1;
for (bas=1,i=N; i; --i,(bas*=10)%=P) {
(sum[i]=sum[i+1]+bas*(s[i]-48)%P)%=P;
if (!mp[sum[i]]) mp[sum[i]]=++cnt;
}
if (!mp[sum[N+1]]) mp[sum[N+1]]=++cnt;
for (int i=1; i<=N+1; ++i) V[i]=mp[sum[i]];
for (Q=read(),i=1; i<=Q; ++i) a[i]=(node){read(),read()+1,i};
std::sort(a+1,a+1+Q);
for (R=ans=0,L=1,i=1; i<=Q; ++i) {
while (L<a[i].L) revise(L++,-1);
while (L>a[i].L) revise(--L,+1);
while (R<a[i].R) revise(++R,+1);
while (R>a[i].R) revise(R--,-1);
Ans[a[i].id]=ans;
}
for (i=1; i<=Q; ++i) printf("%lld\n",Ans[i]);
return 0;
}