[HNOI2016] 大数 - 莫队
Description
给定一个可能有前导零的数字串 S,给定一个素数 p,有 m 个询问,每次询问求 S 的一个子串中有多少个子串是 p 的倍数。
Solution
考虑每个后缀构成的数字,设为 (suf[i]),那么 (S[l:r]) 满足条件即 (frac {suf[l]-suf[r+1]} {10^{r-l+1}} = 0 mod p)
因为数字串是十进制的,所以 10 的因子 2,5 需要被特殊考虑,这时 10 的整数幂没有逆元,消去律失效
当 (p eq 2,5) 时,那么后缀 (suf[l]-suf[r+1]=0 mod p) 即可
当 (p=2) 时,只需要保证结尾位置是偶数即可;当 (p=5) 时,只需要保证结尾位置是 5 的倍数即可
因此这两种情况都可以很容易地特判掉
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 4e5 + 5;
int n, m, p;
string str;
int block_size = 0;
// 莫队算法解决 p neq 2,5
struct MoSolution
{
vector<int> suf;
vector<int> ans;
struct Bucket
{
vector<int> cnt;
int ans;
Bucket()
{
ans = 0; // !!!!!!!!!!!!!
}
void add(int x)
{
ans -= cnt[x] * (cnt[x] - 1) / 2;
cnt[x]++;
ans += cnt[x] * (cnt[x] - 1) / 2;
}
void dec(int x)
{
ans -= cnt[x] * (cnt[x] - 1) / 2;
cnt[x]--;
ans += cnt[x] * (cnt[x] - 1) / 2;
}
} bucket;
struct Question
{
int l, r, id;
static int get_belong(int x)
{
return x / block_size;
}
friend bool operator<(const Question &lhs, const Question &rhs)
{
if (get_belong(lhs.l) == get_belong(rhs.l))
if (get_belong(lhs.l) & 1)
return lhs.r < rhs.r;
else
return lhs.r > rhs.r;
return lhs.l < rhs.l;
}
};
vector<Question> questions;
void solve()
{
suf.resize(n + 2);
bucket.cnt.resize(n + 2);
// 预处理 suf 并离散化之
int base = 1;
map<int, int> disc_mp;
for (int i = n; i >= 1; i--)
{
suf[i] = ((str[i - 1] - '0') * base + suf[i + 1]) % p;
disc_mp[suf[i]]++;
base = (base * 10) % p;
}
int disc_ind = 0;
for (auto &pr : disc_mp)
pr.second = ++disc_ind;
for (int i = 1; i <= n + 1; i++)
suf[i] = disc_mp[suf[i]];
// 读入询问并排序
int m;
cin >> m;
ans.resize(m + 2);
questions.resize(m + 2);
for (int i = 1; i <= m; i++)
{
auto &question = questions[i];
cin >> question.l >> question.r;
question.id = i;
}
sort(&questions[1], &questions[m + 1]);
// 处理询问
int l = 1, r = 0;
for (int i = 1; i <= m; i++)
{
auto [ql, qr, qid] = questions[i];
qr++; // !!!!!!!!
while (r < qr)
bucket.add(suf[++r]);
while (l > ql)
bucket.add(suf[--l]);
while (r > qr)
bucket.dec(suf[r--]);
while (l < ql)
bucket.dec(suf[l++]);
ans[qid] = bucket.ans;
}
for (int i = 1; i <= m; i++)
{
cout << ans[i] << endl;
}
}
};
// 前缀和统计解决 p = 2,5
struct SpecSolution
{
void solve()
{
vector<int> sum_num(n + 2), sum_pos(n + 2);
array<bool, 10> eff;
for (int i = 0; i < 10; i++)
if (i % p == 0)
eff[i] = 1;
for (int i = 1; i <= n; i++)
{
int num = str[i - 1] - '0';
sum_num[i] = sum_num[i - 1] + eff[num];
sum_pos[i] = sum_pos[i - 1] + i * eff[num];
}
int m;
cin >> m;
for (int i = 1; i <= m; i++)
{
int l, r;
cin >> l >> r;
cout << sum_pos[r] - sum_pos[l - 1] - (sum_num[r] - sum_num[l - 1]) * (l - 1) << endl;
}
}
};
// 主程序
signed main()
{
ios::sync_with_stdio(false);
cin >> p >> str;
n = str.length();
block_size = sqrt(n);
if (p == 2 || p == 5)
{
SpecSolution solution;
solution.solve();
}
else
{
MoSolution solution;
solution.solve();
}
}