题目链接:D. Destiny
题意:输入一个长度为n的序列,保证序列里的元素都是<=n的,然后q次询问,
每次输入l,r,k,询问区间内出现次数大于(r-l+1)/k的最小元素,没有的话是-1。
n,q<=3e5,2<=k<=5。
拿主席树维护数字出现次数,查询的时候二分区间去查,如果左端区间可能满足的话去左边找,如果没找到答案再去右边找。
我想着是这种查询最坏会把树的每个节点遍历一遍,单次查询复杂度就很很高,实际上远远达不到这个复杂度,出题人证明了单次查询复杂度为k*logn,暂时还没看懂。
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+7;
struct Tree{
int lc,rc;
int sum;
}tree[maxn*30];
int root[maxn];
int tot;
int build(int l,int r){
int k=++tot;
tree[k].sum=0;
if(l==r) return k;
int mid=(l+r)>>1;
tree[k].lc=build(l,mid);
tree[k].rc=build(mid+1,r);
return k;
}
void pushup(int k){
tree[k].sum=tree[tree[k].lc].sum+tree[tree[k].rc].sum;
}
int updata(int p,int l,int r,int id,int val){
int k=++tot;
tree[k]=tree[p];
if(l==r){
tree[k].sum+=val;
return k;
}
int mid=(l+r)>>1;
if(id<=mid) tree[k].lc=updata(tree[p].lc,l,mid,id,val);
else tree[k].rc=updata(tree[p].rc,mid+1,r,id,val);
pushup(k);
return k;
}
int myfind(int p,int q,int l,int r,int x){
if(l==r){
if(tree[p].sum-tree[q].sum>x) return l;
return -1;
}
int mid=(l+r)>>1;
int res=-1;
if(tree[tree[p].lc].sum-tree[tree[q].lc].sum>x)
res=myfind(tree[p].lc,tree[q].lc,l,mid,x);
if(res!=-1) return res;
if(tree[tree[p].rc].sum-tree[tree[q].rc].sum>x)
return myfind(tree[p].rc,tree[q].rc,mid+1,r,x);
return -1;
}
int main(){
tot=0;
int n,q;
scanf("%d%d",&n,&q);
root[0]=build(1,n);
int x;
int l,r,k;
for(int i=1;i<=n;++i){
scanf("%d",&x);
root[i]=updata(root[i-1],1,n,x,1);
}
while(q--){
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",myfind(root[r],root[l-1],1,n,(r-l+1)/k));
}
return 0;
}