[HNOI2016] 大数

[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();
    }
}
原文地址:https://www.cnblogs.com/mollnn/p/14351652.html