原题链接:D. Destiny
题目大意:
给出一个长度为 n n n 的数组 a a a,再给出 q q q 次询问。
每次询问给出三个参数 l , r , k l,r,k l,r,k ,询问区间 [ l , r ] [l,r] [l,r] 中是否存在某个数字 a i a_{i} ai 的出现次数大于等于 ⌈ r − l + 1 k ⌉ \lceil \frac{r-l+1}{k}\rceil ⌈kr−l+1⌉ ,如果存在则输出最小的那个数字,否则输出 − 1 -1 −1 。
解题思路:
看到出现次数,种类数之类的东西就可以联想到主席树。不过这题有很多写法,随机化,莫队之类的做法,这里讲一下主席树的做法。
我们先按照下标构建一颗主席树,维护每个数字出现的次数,这样我们就可以通过相减来获得区间的某个数字的出现次数。
关于询问,我们考虑做这样一个暴力:
- 因为是求最小值,所以我们优先暴力地递归左儿子,前提是左儿子所有数字出现次数的和大于等于 ⌈ r − l + 1 k ⌉ \lceil \frac{r-l+1}{k}\rceil ⌈kr−l+1⌉。
- 假设左儿子不满足条件或者没找到对应要找的数字,那么继续暴力地递归右儿子,前提是左儿子所有数字出现次数的和大于等于 ⌈ r − l + 1 k ⌉ \lceil \frac{r-l+1}{k}\rceil ⌈kr−l+1⌉。
- 假设左右儿子都不满足就返回一个无穷大的值。
这样做的复杂度看似是 O ( n 2 log n ) O(n^{2} \log n) O(n2logn) 的,实则不然。
注意到我们每次递归的前提是,要满足所有数字出现次数的和大于等于 ⌈ r − l + 1 k ⌉ \lceil \frac{r-l+1}{k}\rceil ⌈kr−l+1⌉ 才会进入儿子节点。
在这里我们分析一种最坏的情况,假设我们每次都必须要递归到叶子节点(或刚好递归到叶子节点的上一层)才能返回。
那么此时肯定是在区间 [ l , r ] [l,r] [l,r] 内有 k k k 个数字都满足出现次数 c n t a i ≥ ⌈ r − l + 1 k ⌉ cnt_{a_i} \geq \lceil \frac{r-l+1}{k}\rceil cntai≥⌈kr−l+1⌉ 的情况。
显然复杂度此时最坏相当于做了 k k k 次独立的查询,因此单次查询最坏复杂度为 O ( k log n ) O(k\log n) O(klogn) ,而不是 O ( n log n ) O(n \log n) O(nlogn)。
显然可以通过,注意一些代码细节即可
时间复杂度: O ( q k log n ) O(qk\log n) O(qklogn)
#include <bits/stdc++.h>
using namespace std;
using PII = pair<int, int>;
using i64 = long long;
const int N = 3e5 + 1;
struct Segtree {
int l, r, sum;
} seg[N << 5];
#define lson seg[k].l, l, mid
#define rson seg[k].r, mid + 1, r
#define sum(x) seg[x].sum
#define ls(x) seg[x].l
#define rs(x) seg[x].r
int idx = 0;
int creat(int pre) {
seg[++idx] = seg[pre];
return idx;
}
int Modify(int k, int l, int r, int p) {
int cur = creat(k);
++sum(cur);
if (l == r) {
return cur;
}
int mid = l + r >> 1;
if (p <= mid) {
ls(cur) = Modify(lson, p);
} else {
rs(cur) = Modify(rson, p);
}
return cur;
}
const int INF = 1e9;
int Query(int pre, int k, int l, int r, int kth) {
if (l == r) {
return l;
}
//注意一下这里的查询细节
int mid = l + r >> 1, res = INF;
if (sum(ls(k)) - sum(ls(pre)) >= kth) {
res = Query(ls(pre), lson, kth);
}
if (res == INF && sum(rs(k)) - sum(rs(pre)) >= kth) {
res = Query(rs(pre), rson, kth);
}
return res;
}
void solve() {
int n, q;
cin >> n >> q;
vector<int> ver(n + 1);
for (int i = 1; i <= n; ++i) {
int val;
cin >> val;
ver[i] = Modify(ver[i - 1], 1, n, val);
}
for (int i = 1; i <= q; ++i) {
int l, r, k;
cin >> l >> r >> k;
k = (r - l + 1) / k + 1;
int res = Query(ver[l - 1], ver[r], 1, n, k);
if (res == INF) {
cout << -1 << "\n";
} else {
cout << res << "\n";
}
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1; //cin >> t;
while (t--) solve();
return 0;
}