zoukankan      html  css  js  c++  java
  • [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();
        }
    }
    
  • 相关阅读:
    GitLab 重置认证和添加账号缓存
    PHP 正则匹配IP
    git 删除指定版本
    PostgreSQL 9.2 日期运算
    postgre 已有字段修改为自增
    postgresql 导入导出
    PHP TS 和 NTS 版本选择
    background-image属性的设置
    SQLServer 附加数据库后只读或报错解决方法
    IIS 6.0 发布网站使用教程
  • 原文地址:https://www.cnblogs.com/mollnn/p/14351652.html
Copyright © 2011-2022 走看看