problem
luogu-P3245
solution
将这个 nnn 位数从右往左的记录取模 ppp 的结果,即 f(i)=(f(i−1)∗10+si)%pf(i)=(f(i-1)*10+s_i)\% pf(i)=(f(i−1)∗10+si)%p。
显然一个子串是 ppp 的倍数必然满足:f(r)−f(l−1)10r−l+1≡0(modp)\frac{f(r)-f(l-1)}{10^{r-l+1}}\equiv 0\pmod p10r−l+1f(r)−f(l−1)≡0(modp)。
但这并不严谨,虽然好像 101010 和质数是互质的,但是当 p=2/5p=2/5p=2/5 的时候上述式子便不成立了。
但是 2,52,52,5 有着非常特殊的性质,即可以通过数的最后一位判断是否该数是否是其倍数。
所以当 p=2,p=5p=2,p=5p=2,p=5 的时候我们可以直接特殊的在线处理。
否则就可以按照上面这样离线下来。因为存在互质,则等价于 f(r)≡f(l−1)(modp)f(r)\equiv f(l-1)\pmod pf(r)≡f(l−1)(modp)。
这就相当于是 [l,r][l,r][l,r] 区间内数相同点对的个数,莫队经典题目。
注意 ppp 过大,所以还要再来个离散化。
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;
}