Description
给定一个长度为 (n) 的数列 ({a_1,a_2,cdots ,a_n}),(q) 次询问。
每个询问给定一段区间 ([l,r]) 以及一个整数 (k),求该区间中出现次数大于 (frac{r - l + 1}{k}) 次的最小数。如果不存在输出 -1。
Hint
(1le n,qle 3 imes 10^5)
(1le a_ile n, 1le lle rle ,2le kle 5)
Solution
先对数列 (a) 建出可持久化线段树(主席树)。
然后重点是查询。
对于某个询问,设 (c = frac{r - l + 1}{k}) ,那么在主席树的某一个结点:
计算出左右儿子的 size
,即值在左右儿子管辖值域的数的个数,分别记为 (sL,sR)。
由于要求这个数字尽量小,因此先在左子树中查找,再右子树。
但即便如此,那也不是两颗子树都是要搜的。
显然如果 (sL le c) ,那么左边就不可能有答案,没有搜的必要。右边同理。
其他部分就是平平常常的主席树。时空复杂度 (O(nlog n))
Code
/*
* Author : _Wallace_
* Source : https://www.cnblogs.com/-Wallace-/
* Problem : Codeforces 429D Destiny
*/
#include <iostream>
using namespace std;
const int N = 3e5 + 5;
const int S = N << 5;
int lc[S], rc[S];
int size[S];
int total = 0;
int root[N];
#define mid ((l + r) >> 1)
int build(int l, int r) {
int rt = ++total;
if (l == r) return rt;
lc[rt] = build(l, mid);
rc[rt] = build(mid + 1, r);
return rt;
}
int update(int pre, int l, int r, int p) {
int rt = ++total;
size[rt] = size[pre] + 1;
lc[rt] = lc[pre], rc[rt] = rc[pre];
if (l == r) return rt;
if (p <= mid) lc[rt] = update(lc[pre], l, mid, p);
else rc[rt] = update(rc[pre], mid + 1, r, p);
return rt;
}
int query(int s, int t, int l, int r, int c) {
if (l == r) return l;
int sL = size[lc[t]] - size[lc[s]];
int sR = size[rc[t]] - size[rc[s]];
int temp = -1;
if (sL > c)
if ((temp = query(lc[s], lc[t], l, mid, c)) != -1)
return temp;
if (sR > c)
if ((temp = query(rc[s], rc[t], mid + 1, r, c)) != -1)
return temp;
return -1;
}
int n, q;
signed main() {
ios::sync_with_stdio(0);
cin >> n >> q;
root[0] = build(1, n);
for (register int a, i = 1; i <= n; i++)
cin >> a, root[i] = update(root[i - 1], 1, n, a);
while (q--) {
int l, r, k;
cin >> l >> r >> k;
k = (r - l + 1) / k;
cout << query(root[l - 1], root[r], 1, n, k) << endl;
}
return 0;
}