题目大意:
有一个长度为 的序列,有 次询问,每次询问区间 中出现次数 严格大于 区间长度除以 (即 )的元素中,最小的那个数是多少。若不存在这样的元素输出-1。
,
题解
用一个数组存数字(离散化一次)的出现次数,在其上建主席树。向下询问时值小于指定值时返回
- 这样至多只会访问K个叶子节点(不可能一段区间里面出现超过K个数值个数大于 区间长度/K 的)。K很小,所以可以去枚举。
- 有一点可以优化就是如果左节点找到答案就不用去搜索右节点了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define maxn 300010
using namespace std;
const int M = maxn * 30;
int n,q,m,tot,ans;
int a[maxn], t[maxn];
int T[maxn], lson[M], rson[M], c[M];
void Init_hash(){
for(int i=1;i<=n;i++)
t[i] = a[i];
sort(t+1,t+1+n);
m = (int)(unique(t+1, t+1+n)-t-1);
}
int build(int l, int r)
{
int root = tot++;
c[root] = 0;
if(l!=r){
int mid = (l+r)>>1;
lson[root] = build(l, mid);
rson[root] = build(mid+1,r);
}
return root;
}
int mapp(int x){
return (int)(lower_bound(t+1, t+1+m, x) - t);
}
int update(int root, int pos, int val){
int newroot = tot++, tmp = newroot;
c[newroot] = c[root] + val;
int l = 1, r = m;
while(l < r){
int mid = (l+r)>>1;
if(pos<=mid){
lson[newroot] = tot++; rson[newroot] = rson[root];
newroot = lson[newroot]; root = lson[root];
r = mid;
}
else{
rson[newroot] = tot++; lson[newroot] = lson[root];
newroot = rson[newroot]; root = rson[root];
l = mid+1;
}
c[newroot] = c[root] + val;
}
return tmp;
}
void query(int left_root, int right_root, int l, int r, int k){
if(c[right_root]-c[left_root] < k) return;
if(l==r){
if(c[right_root]-c[left_root] >= k) ans = min(ans, l);
return;
}
int mid = (l+r)>>1;
query(lson[left_root], lson[right_root], l, mid, k);
if(ans==0x3f3f3f3f) query(rson[left_root], rson[right_root], mid+1, r, k);
}
int main()
{
tot = 0;
scanf("%d %d", &n, &q);
for(int i=1;i<=n;i++)
scanf("%d", &a[i]);
Init_hash();
T[0] = build(1, m);
for(int i=1;i<=n;i++){
int pos = mapp(a[i]);
T[i] = update(T[i-1], pos, 1);
}
while(q--){
ans = 0x3f3f3f3f;
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
int v = (r-l+1)/k + 1;
query(T[l-1], T[r], 1, m, v);
if(ans==0x3f3f3f3f) puts("-1");
else printf("%d\n", t[ans]);
}
return 0;
}