P5629 【AFOI-19】区间与除法 题解

发布时间 2023-08-28 10:44:41作者: MoyouSayuki

P5629 【AFOI-19】区间与除法 题解

由于题目中的运算是除法,所以对于一个数字 \(x\),最多运算次数不会超过 \(\lceil\log_{d}x\rceil\) 就会变成 \(0\)

然后我们就可以在 \(O(n\log C)\) 的时间复杂度内算出来每一个数字能被哪些原数消灭。

这样处理询问仍然棘手,接下来有一个性质:

如果原数 \(a\) 可以被操作为原数 \(b\),那么所有能被 \(a\) 消灭的数字都可以被 \(b\) 消灭。

证明显然,如果数字 \(x\) 能被 \(a\) 消灭,那么它一定会在某一个时刻变成 \(a\),而 \(a\) 可以被 \(b\) 消灭,所以 \(x\) 变成 \(a\) 后可以被 \(b\) 消灭。

所以可以把原数全部去重然后只保留不会被其它原数消灭的原数。

这样每一个序列中的数 \(a_i\) 最多对应一个原数可以把它消灭。

对于没法消灭的 \(a_i\) 不用管,询问就变成了区间不同数字的个数,由于 \(m\le 60\),可以暴力枚举每一个原数判断是否存在于区间内,这个用前缀和实现即可。

时间复杂度:\(O(n\log C+nm+qm)\)

// Problem: P5629 【AFOI-19】区间与除法
// Author: Moyou
// Copyright (c) 2023 Moyou All rights reserved.
// Date: 2023-08-28 09:58:44

#include <algorithm>
#include <cstring>
#include <iostream>
#include <unordered_map>
#define int long long
using namespace std;
const int N = 5e5 + 10, M = 70;
int n, m, d, q, a[N], b[N], idx, temp[N], tot;
unsigned s[N][M];
unordered_map<int, bool> h;
int find(int x) {
    return lower_bound(b + 1, b + idx + 1, x) - b;
}

signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n >> m >> d >> q;
    for(int i = 1; i <= n; i ++) cin >> a[i];
    for(int i = 1; i <= m; i ++) cin >> b[i];
    sort(b + 1, b + m + 1);
    tot = unique(b + 1, b + m + 1) - b - 1;
    for(int i = 1; i <= tot; i ++) {
        int t = b[i];
        while(t) {
            if(h.count(t)) {
                b[i] = -1;
                break;
            }
            t /= d;
        }
        if(~b[i]) h[b[i]] = 1;
    }
    swap(temp, b);
    for(int i = 1; i <= tot; i ++)
        if(~temp[i]) b[++ idx] = temp[i];
    for(int i = 1; i <= n; i ++) {
        int t = a[i];
        a[i] = -1;
        while(t) {
            if(h.count(t)) {
                a[i] = t;
                break;
            }
            t /= d;
        }
    }
    for(int i = 1; i <= n; i ++) {
        memcpy(s[i], s[i - 1], sizeof s[i - 1]);
        if(~a[i]) s[i][find(a[i])] ++;
    }
    for(int i = 1, l, r, ans = 0; i <= q; i ++, ans = 0) {
        cin >> l >> r;
        for(int j = 1; j <= idx; j ++) 
            if(s[r][j] - s[l - 1][j]) ans ++;
        cout << ans << '\n';
    }
    return 0;
}