【HNOI2016】大数
题面
题解
首先考虑如何判断一个区间内的数是否为一个数的倍数。
设\(x_i\)表示区间\([i, n]\)组成的数。
如果\([l, r]\)内的数是质数\(p\)的质数,则:
\[\frac{x_l - x_{r + 1}}{10 ^ {r - l + 1}} \equiv 0 \mod p
\]
当\(p \neq 2, 5\)时,\(\gcd(10, p) = 1\)当且仅当
\[x_l - x_{r + 1} \equiv 0 \mod p
\]
\[\Leftrightarrow x_l \equiv x_{r + 1} \mod p
\]
时,区间\([l, r]\)内的数是\(p\)的倍数。
当\(p = 2\)或\(p = 5\)时,可以只通过最后一位有关,很好判断。
于是这两种情况都可以直接莫队。
注意权值要离散化。
代码
#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<algorithm>
#define RG register
#define clear(x, y) memset(x, y, sizeof(x))
const int maxn(100010);
struct qry { int l, r, blk, id; } q[maxn];
long long ans, Ans[maxn]; char s[maxn];
int val[maxn], a[maxn], Pow[maxn], num[maxn];
int n, m, p, SIZE, L, R, cnt, tot;
inline bool cmp(const qry &x, const qry &y)
{
return x.blk == y.blk ? (x.blk & 1 ? x.r > y.r : x.r < y.r)
: x.blk < y.blk;
}
inline void insert(int x) { ans += num[val[x]], ++num[val[x]]; }
inline void erase(int x) { --num[val[x]], ans -= num[val[x]]; }
inline void insl(int x) { cnt += (s[x] - '0') % p == 0, ans += cnt; }
inline void dell(int x) { ans -= cnt, cnt -= (s[x] - '0') % p == 0; }
inline void insr(int x) { if((s[x] - '0') % p == 0) ++cnt, ans += R - L + 1; }
inline void delr(int x) { if((s[x] - '0') % p == 0) ans -= R - L + 1, --cnt; }
int main()
{
scanf("%d%s%d", &p, s + 1, &m); n = strlen(s + 1); Pow[0] = 1;
for(RG int i = 1; i <= n; i++) Pow[i] = 10ll * Pow[i - 1] % p;
for(RG int i = n; i; i--) a[i] = val[i] = (val[i + 1] +
1ll * (s[i] - '0') * Pow[n - i]) % p;
std::sort(a + 1, a + n + 2); int _n = std::unique(a + 1, a + n + 2) - a - 1;
for(RG int i = 1; i <= n; i++)
val[i] = std::lower_bound(a + 1, a + _n + 1, val[i]) - a - 1;
SIZE = ceil(sqrt(n));
for(RG int i = 1; i <= m; i++)
scanf("%d%d", &q[i].l, &q[i].r),
q[i].blk = q[i].l / SIZE, q[i].id = i;
std::sort(q + 1, q + m + 1, cmp); L = 1, R = 0;
if(p != 2 && p != 5) for(RG int i = 1; i <= m; i++)
{
++q[i].r;
while(L > q[i].l) --L, insert(L);
while(R < q[i].r) ++R, insert(R);
while(L < q[i].l) erase(L), ++L;
while(R > q[i].r) erase(R), --R;
Ans[q[i].id] = ans;
}
else for(RG int i = 1; i <= m; i++)
{
while(L > q[i].l) --L, insl(L);
while(R < q[i].r) ++R, insr(R);
while(L < q[i].l) dell(L), ++L;
while(R > q[i].r) delr(R), --R;
Ans[q[i].id] = ans;
}
for(RG int i = 1; i <= m; i++) printf("%lld\n", Ans[i]);
return 0;
}