题目
题目大意:一个区间内有n个数字,有m个询问,每次询问一个区间。回答这个区间的第k大的热度值。
热度值定义:数字i的热度值为:这个区间内i的出现次数。
分析
正解前置知识
权值分块;莫队算法
首先暴力做法:对于每一个区间,暴力搜索出每个数字的出现次数,把出现次数push到一个数组里面,寻找到出现次数的第k大的数字。
优化一:权值分块
优化寻找第k大数字,可以采用权值分块的方法。
一共n个数字,因此热度值的取值范围是 [ 0 , n ] [0,n] [0,n],所以把这个区间进行分块,维护每个块中热度值分布情况以及总的热度值个数。这样可以在 n \sqrt{n} n的复杂度内求出第k大热度值。
具体做法:(qu函数)
1.从前往后遍历每个块,统计当前热度值个数。
2.如果加上当前块之后热度值个数大于k,就开始遍历块中的热度值
优化二:莫队算法离线处理
由于这道题目没有强制在线,因此可考虑莫队算法。
具体的add、remove操作见代码
注意由于数据规模为1e9,因此需要离散化
代码
#include<bits/stdc++.h>
using namespace std;
int read(){
char s;
int x=0,f=1;
s=getchar();
while(s<'0'||s>'9'){
if(s=='-')f=-1;
s=getchar();
}
while(s>='0'&&s<='9'){
x*=10;
x+=s-'0';
s=getchar();
}
return x*f;
}
const int N=1e5+5;
int n,m;
struct query{
int l,r,k;
int id;
}q[N];
int a[N];
int block,num;
int belong[N];
int ls[N],rs[N];
void build(){
block=sqrt(n);
num=n/block;
if(n%block)num++;
for(int i=1;i<=n;i++)belong[i]=(i-1)/block+1;
for(int i=1;i<=num;i++){
ls[i]=(i-1)*block+1;
rs[i]=i*block;
}
rs[num]=n;
}
int pos[N];//离散化
int cnt[N];//记录数字i出现了几次
int tot[N];//值域分块-单点 tot[i]表示出现过i次的数字有几个(热度值为i的有几个)
int sum[N];//值域分块-分块
int all;//有几个热度值
void modify(int x,int c){//x位置 +c
tot[x]+=c;
sum[belong[x]]+=c;
all+=c;
}
int qu(int k){
if(all<k)return -1;
int id=1;
for(int i=1;i<=num;i++){
if(k>sum[i])k-=sum[i];
else{
for(int j=ls[i];j<=rs[i];j++){
if(k>tot[j])k-=tot[j];
else return j;
}
}
}
return k;
}
void add(int x){
if(cnt[a[x]])modify(cnt[a[x]],-1);//出现了这种次数的数字-1
cnt[a[x]]++;
modify(cnt[a[x]],1);
}
void remove(int x){
modify(cnt[a[x]],-1);
cnt[a[x]]--;
if(cnt[a[x]])modify(cnt[a[x]],1);
}
bool operator<(query a,query b){
if(belong[a.l]!=belong[b.l])return belong[a.l]<belong[b.l];
return a.r<b.r;
}
int ans[N];
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++){
a[i]=read();
pos[i]=a[i];
}
sort(pos+1,pos+1+n);
int len=unique(pos+1,pos+1+n)-1-pos;
for(int i=1;i<=n;i++)a[i]=lower_bound(pos+1,pos+1+n,a[i])-pos;//离散化
for(int i=1;i<=m;i++){
q[i].l=read(),q[i].r=read(),q[i].k=read();
q[i].id=i;
}
build();
sort(q+1,q+1+m);
int l=1,r=0;
for(int i=1;i<=m;i++){
while(l<q[i].l)remove(l++);
while(l>q[i].l)add(--l);
while(r<q[i].r)add(++r);
while(r>q[i].r)remove(r--);
ans[q[i].id]=qu(q[i].k);
}
for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
}