problem
solution
将这个 n n n 位数从右往左的记录取模 p p p 的结果,即 f ( i ) = ( f ( i − 1 ) ∗ 10 + s i ) % p f(i)=(f(i-1)*10+s_i)\% p f(i)=(f(i−1)∗10+si)%p。
显然一个子串是 p p p 的倍数必然满足: f ( r ) − f ( l − 1 ) 1 0 r − l + 1 ≡ 0 ( m o d p ) \frac{f(r)-f(l-1)}{10^{r-l+1}}\equiv 0\pmod p 10r−l+1f(r)−f(l−1)≡0(modp)。
但这并不严谨,虽然好像 10 10 10 和质数是互质的,但是当 p = 2 / 5 p=2/5 p=2/5 的时候上述式子便不成立了。
但是 2 , 5 2,5 2,5 有着非常特殊的性质,即可以通过数的最后一位判断是否该数是否是其倍数。
所以当 p = 2 , p = 5 p=2,p=5 p=2,p=5 的时候我们可以直接特殊的在线处理。
否则就可以按照上面这样离线下来。因为存在互质,则等价于 f ( r ) ≡ f ( l − 1 ) ( m o d p ) f(r)\equiv f(l-1)\pmod p f(r)≡f(l−1)(modp)。
这就相当于是 [ l , r ] [l,r] [l,r] 区间内数相同点对的个数,莫队经典题目。
注意 p p p 过大,所以还要再来个离散化。
code
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define maxn 200005
struct node { int l, r, id; }q[maxn];
int n, p, m, B, ans;
char s[maxn];
int f[maxn], g[maxn], h[maxn], block[maxn], a[maxn], b[maxn], cnt[maxn], ret[maxn];
void add( int x ) {
ans -= cnt[a[x]] * ( cnt[a[x]] - 1 ) / 2;
cnt[a[x]] ++;
ans += cnt[a[x]] * ( cnt[a[x]] - 1 ) / 2;
}
void sub( int x ) {
ans -= cnt[a[x]] * ( cnt[a[x]] - 1 ) / 2;
cnt[a[x]] --;
ans += cnt[a[x]] * ( cnt[a[x]] - 1 ) / 2;
}
signed main() {
scanf( "%lld %s %lld", &p, s + 1, &m );
int n = strlen( s + 1 );
if( p == 2 or p == 5 ) {
if( p == 2 ) h[0] = h[2] = h[4] = h[6] = h[8] = 1;
else h[0] = h[5] = 1;
for( int i = 1;i <= n;i ++ ) {
f[i] = f[i - 1] + h[s[i] ^ 48];
g[i] = g[i - 1] + h[s[i] ^ 48 ] * i;
}
for( int i = 1, l, r;i <= m;i ++ ) {
scanf( "%lld %lld", &l, &r );
printf( "%lld\n", g[r] - g[l - 1] - ( f[r] - f[l - 1] ) * ( l - 1 ) );
}
return 0;
}
B = sqrt( n );
for( int i = 1;i <= n + 1;i ++ ) block[i] = ( i - 1 ) / B + 1;
for( int i = 1;i <= m;i ++ ) scanf( "%lld %lld", &q[i].l, &q[i].r ), q[i].r ++, q[i].id = i;
sort( q + 1, q + m + 1, []( node x, node y ) { return block[x.l] == block[y.l] ? x.r < y.r : x.l < y.l; } );
for( int i = n, x = 1;i;i --, x = x * 10 % p ) {
a[i] = ( ( s[i] ^ 48 ) * x + a[i + 1] ) % p;
b[i] = a[i];
}
sort( b + 1, b + n + 2 );
int t = unique( b + 1, b + n + 2 ) - b - 1;
for( int i = 1;i <= n + 1;i ++ ) a[i] = lower_bound( b + 1, b + t + 1, a[i] ) - b;
int curl = 1, curr = 0;
for( int i = 1;i <= m;i ++ ) {
int l = q[i].l, r = q[i].r;
while( curl < l ) sub( curl ++ );
while( curl > l ) add( -- curl );
while( curr < r ) add( ++ curr );
while( curr > r ) sub( curr -- );
ret[q[i].id] = ans;
}
for( int i = 1;i <= m;i ++ ) printf( "%lld\n", ret[i] );
return 0;
}