P5629 【AFOI-19】区间与除法 题解
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;
}