P3245:「HNOI2016」大数 – 题解

主要思路

佛了。

让 \(p\) 跟 \(10\) 取个 \(\gcd\)。如果互质的话就是数数列 \(a_i = \text{从 i 到 n 表示的数字} \bmod p\) 区间里的同色对数,用莫队搞即可;如果是 \(2\) 那就把 \(0, 2, 4, 6, 8\) 这些尾数染颜色然后数同色对数,这个可以 \(\Theta(n)\),如果是 \(5\) 那就把 \(0, 5\) 染色即可。

代码

// P3245.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 2e5 + 200;

typedef long long ll;

int n, p, suf[MAX_N], q, block;
char str[MAX_N];

namespace Subtask1
{

struct queryInfo
{
    int l, r, id;
    bool operator<(const queryInfo &rhs) const { return int(l / block) < int(rhs.l / block) || (int(l / block) == int(rhs.l / block) && r < rhs.r); }
} queries[MAX_N];

vector<int> mpping;
ll curt, ansBox[MAX_N], mp[MAX_N];

void add(int pos)
{
    curt -= mp[pos] * (mp[pos] - 1) / 2;
    mp[pos]++;
    curt += mp[pos] * (mp[pos] - 1) / 2;
}

void remove(int pos)
{
    curt -= mp[pos] * (mp[pos] - 1) / 2;
    mp[pos]--;
    curt += mp[pos] * (mp[pos] - 1) / 2;
}

int handler()
{
    for (int i = 1; i <= q; i++)
        scanf("%d%d", &queries[i].l, &queries[i].r), queries[i].r++, queries[i].id = i;
    block = sqrt(q), sort(queries + 1, queries + 1 + q);
    int cl = 1, cr = 0;
    for (int i = 0; i <= n + 1; i++)
        mpping.push_back(suf[i]);
    sort(mpping.begin(), mpping.end()), mpping.erase(unique(mpping.begin(), mpping.end()), mpping.end());
    for (int i = 0; i <= n + 1; i++)
        suf[i] = lower_bound(mpping.begin(), mpping.end(), suf[i]) - mpping.begin() + 1;
    for (int i = 1; i <= q; i++)
    {
        while (cl < queries[i].l)
            remove(suf[cl++]);
        while (cl > queries[i].l)
            add(suf[--cl]);
        while (cr < queries[i].r)
            add(suf[++cr]);
        while (cr > queries[i].r)
            remove(suf[cr--]);
        ansBox[queries[i].id] = curt;
    }
    for (int i = 1; i <= q; i++)
        printf("%lld\n", ansBox[i]);
    return 0;
}

} // namespace Subtask1

namespace Subtask2
{

int digits[10];
ll pre[MAX_N], pre_pos[MAX_N];

int handler()
{
    if (__gcd(p, 10) == 2)
        digits[0] = digits[2] = digits[4] = digits[6] = digits[8] = 1;
    else
        digits[0] = digits[5] = 1;
    for (int i = 1; i <= n; i++)
    {
        pre[i] = pre[i - 1] + digits[str[i] - '0'];
        pre_pos[i] = pre_pos[i - 1] + digits[str[i] - '0'] * i;
    }
    while (q--)
    {
        int l, r;
        scanf("%d%d", &l, &r);
        printf("%lld\n", pre_pos[r] - pre_pos[l - 1] - (pre[r] - pre[l - 1]) * (l - 1));
    }
    return 0;
}

} // namespace Subtask2

int main()
{
    scanf("%d%s%d", &p, str + 1, &q), n = strlen(str + 1);
    for (int i = n, bas = 1; i >= 1; i--, bas = 10LL * bas % p)
        suf[i] = (1LL * suf[i + 1] + 1LL * bas * (str[i] - '0') % p) % p;
    int d = __gcd(p, 10);
    if (d == 1)
        return Subtask1::handler();
    else
        return Subtask2::handler();
    return 0;
}

Leave a Reply

Your email address will not be published. Required fields are marked *