像这样“子串的子串”“区间内的区间”的问题,线段树这样的数据结构不容易解决。于是想到莫队。
而现在用莫队算法的关键在于移动指针。也就是:
1、求[l,l],[l,l+1],...,[l,r−1],[l,r]中有多少个子串是P的倍数。
2、求
先设
这时候可以得到sum[l]−sum[r+1]=num[l,r]∗10n−r。
当P与
这样就变成了询问「一个区间内有多少个数等于一个定值」的问题。可以将sum的值离散化,在莫队不断移动指针的时候统计每个sum值出现的次数。
当然,还要特判P=2或P=5的情况,这种情况时,可以利用最低位进行统计。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
typedef long long ll;
const int N = 1e5 + 5, E = 13;
int S, ZZQ, n, m, q, a[N], cnt[N], sum[N], tmp[N], pw[N], orz[N][E];
char s[N]; ll now, res[N];
struct cyx {
int l, r, bl, id;
friend inline bool operator < (cyx a, cyx b) {
if (a.bl != b.bl) return a.bl < b.bl;
return a.r < b.r;
}
} que[N];
int pre(int l, int r) {
if (ZZQ == 2 || ZZQ == 5)
return orz[r][0] - orz[l - 1][0];
return cnt[sum[l]] + (sum[r + 1] == sum[l]) - 1;
}
int suf(int l, int r) {
if (ZZQ == 2 || ZZQ == 5)
return orz[r][0] - orz[r - 1][0] ? r - l + 1 : 0;
return cnt[sum[r + 1]];
}
int main() {
int i, j, l = 1, r = 0; ZZQ = read(); scanf("%s", s + 1); pw[0] = 1;
n = strlen(s + 1); for (i = 1; i <= n; i++) a[i] = s[i] - '0';
for (i = 1; i <= n; i++) pw[i] = 10ll * pw[i - 1] % ZZQ;
S = sqrt(n); for (i = n; i; i--) sum[i] =
(1ll * a[i] * pw[n - i] % ZZQ + sum[i + 1]) % ZZQ;
m = read(); for (i = 1; i <= m; i++)
que[i].bl = ((que[i].l = read()) - 1) / S + 1,
que[i].r = read(), que[i].id = i;
for (i = 1; i <= n + 1; i++) tmp[i] = sum[i];
sort(tmp + 1, tmp + n + 2); q = unique(tmp + 1, tmp + n + 2) - tmp - 1;
for (i = 1; i <= n + 1; i++)
sum[i] = lower_bound(tmp + 1, tmp + q + 1, sum[i]) - tmp;
for (i = 1; i <= n; i++) {
int x = a[i] % ZZQ;
for (j = 0; j < 10; j++)
orz[i][j] = orz[i - 1][j] + (j == x);
}
sort(que + 1, que + m + 1); for (i = 1; i <= m; i++) {
int tl = que[i].l, tr = que[i].r;
while (r < tr) cnt[sum[++r]]++, now += suf(l, r);
while (l > tl) cnt[sum[--l]]++, now += pre(l, r);
while (r > tr) now -= suf(l, r), cnt[sum[r--]]--;
while (l < tl) now -= pre(l, r), cnt[sum[l++]]--;
res[que[i].id] = now;
}
for (i = 1; i <= m; i++) printf("%lld\n", res[i]);
return 0;
}