题面
样例
11
121121
3
1 6
1 5
1 4
5
3
2
分析
易知 [ l , r ] = ∑ a [ i ] ∗ 1 0 r − i ≡ 0 ( m o d p ) [l,r]=\sum a[i]*10^{r-i}\equiv 0\ (mod\ p) [l,r]=∑a[i]∗10r−i≡0 (mod p)
-
p p p 与 10 10 10 不互质,又因为 p p p 为质数,所以 p = 2 p=2 p=2 或 p = 5 p=5 p=5,这种情况 [ l , r ] ≡ a [ 1 ] ≡ 0 ( m o d p ) [l,r]\equiv a[1]\equiv\ 0\ (mod\ p) [l,r]≡a[1]≡ 0 (mod p), 则只要 p ∣ a [ 1 ] p|a[1] p∣a[1] 即可。
-
反之,发现原来的式子不好往下延展,不妨令 p r e [ i ] = [ i , n ] pre[i]=[i,n] pre[i]=[i,n] 的数值(套路)。则 [ i , j ] = ( p r e [ i ] − p r e [ j + 1 ] ) / 1 0 j − i + 1 ≡ 0 ( m o d p ) [i,j]=(pre[i]-pre[j+1]) / 10^{j-i+1} \equiv 0\ (mod\ p) [i,j]=(pre[i]−pre[j+1])/10j−i+1≡0 (mod p)
由于 1 0 i − j 10^{i-j} 10i−j 的逆元等于 ( 1 0 j − i + 1 ) p − 2 (10^{j-i+1})^{p-2} (10j−i+1)p−2,一定与 p p p 互质,所以 p r e [ i ] − p r e [ j + 1 ] ≡ 0 ( m o d p ) pre[i]-pre[j+1]\equiv 0\ (mod\ p) pre[i]−pre[j+1]≡0 (mod p),即求一个区间内有多少对数字的值相同。这就是莫队的板子了。(注意询问的 r r r 要加 1 1 1 )
Code
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <climits>
#define LL long long
using namespace std;
const int MAXN = 2e5 + 5;
int p, m, t, a[MAXN], lsh[MAXN], f, l = 1, r = 0, c[MAXN], n, pre[MAXN];
char s[MAXN];
LL ans[MAXN], res;
struct Node {
int X, Y, pos;
bool operator < (const Node P) const { return X / t != P.X / t ? X < P.X : Y < P.Y; }
}query[MAXN];
void Add(int x, int F) {
if(f) c[a[x]] ++, F ? res += (a[x] % p == 0) * (r - l + 1) : res += c[a[x]];
else res += c[pre[x]], c[pre[x]] ++;
}
void Del(int x, int F) {
if(f) F ? res -= (a[x] % p == 0) * (r - l + 1 + 1) : res -= c[a[x]], c[a[x]] --;
else c[pre[x]] --, res -= c[pre[x]];
}
int main() {
int qwq = 1;
scanf("%d%s%d", &p, s + 1, &m); n = strlen(s + 1); t = sqrt(n);
for(int i = 1; i <= n; i ++) a[i] = s[i] - '0';
for(int i = n; i >= 1; i --) pre[i] = (LL)(pre[i + 1] + (LL)qwq * a[i]) % p, qwq = ((LL)qwq * 10) % p, lsh[i] = pre[i];
for(int i = 1; i <= m; i ++) scanf("%d%d", &query[i].X, &query[i].Y), query[i].Y ++, query[i].pos = i;
sort(query + 1, query + 1 + m);
sort(lsh + 1, lsh + 2 + n);
int len = unique(lsh + 1, lsh + 2 + n) - lsh - 1;
for(int i = 1; i <= n + 1; i ++) pre[i] = lower_bound(lsh + 1, lsh + 1 + len, pre[i]) - lsh;
if(p == 2 || p == 5) f = 1;
for(int i = 1; i <= m; i ++) {
if(f) query[i].Y --;
while(l < query[i].X) Del(l ++, 0);
while(l > query[i].X) Add(-- l, 0);
while(r < query[i].Y) Add(++ r, 1);
while(r > query[i].Y) Del(r --, 1);
ans[query[i].pos] = res;
}
for(int i = 1; i <= m; i ++) printf("%lld\n", ans[i]);
return 0;
}