#6273. 郁金香 题解

题目链接:#6273. 郁金香

题目大意:给定一个长度为 \(n\) 序列 \(\{a_i\}_{i=1}^{n}\)\(m\) 次询问区间 \([l,r]\) 中出现次数第 \(k\) 多的数,如出现次数相同,则令数较小出现次数较多。
\(n,m,a_i\leq 10^5\)


题解:听说有划分树的 \(\text{polylog}\) 做法,可惜我并不会。<-- 不要听博主这个弱智的话,这问题严格强于区间众数。

直接莫队,令块长为 \(S\) ,那么我们考虑将值域分块,这样的话可以在 \(O(S+\frac{n}{S})\) 的时间内求出出现次数。

接下来考虑求出现次数为 \(x\) 的第 \(k\) 小值,所以我们直接对序列分块,令 \(f_{i,j}\) 表示出现次数为 \(i\) 的数在第 \(j\) 块中的个数,容易发现可以在 \(O(S+\frac{n}{S})\) 的时间内解决。

\(S=\sqrt{n}\) 时最优,时间复杂度为 \(O(q\sqrt{n})\)

代码:

#include <cstdio>
#include <algorithm>
using namespace std;
const int Maxv=100000;
const int Maxn=100000;
const int Maxs=334;
const int Maxb=(Maxn-1)/Maxs+1;
int n,m;
struct Question{
	int l,r,k;
	int id;
	friend bool operator <(Question a,Question b){
		if((a.l-1)/Maxs==(b.l-1)/Maxs){
			if(((a.l-1)/Maxs)&1){
				return a.r>b.r;
			}
			return a.r<b.r;
		}
		return a.l<b.l;
	}
}qu[Maxn+5];
int a[Maxn+5];
int f[Maxv+5][Maxb+5];
int cnt[Maxn+5];
int sum[Maxn+5],s_block[Maxb+5];
int ans[Maxn+5];
void add(int x,int a){
	int last=cnt[x];
	cnt[x]+=a;
	f[last][(x-1)/Maxs+1]--;
	f[cnt[x]][(x-1)/Maxs+1]++;
	if(last){
		s_block[(last-1)/Maxs+1]--;
	}
	else{
		s_block[0]--;
	}
	if(cnt[x]){
		s_block[(cnt[x]-1)/Maxs+1]++;
	}
	else{
		s_block[0]++;
	}
	sum[last]--;
	sum[cnt[x]]++;
}
int query(int k){
	int bel;
	for(bel=(n-1)/Maxs+1;k>s_block[bel]&&bel;bel--){
		k-=s_block[bel];
	}
	int num;
	for(num=min(n,bel*Maxs);k>sum[num];num--){
		k-=sum[num];
	}
	for(bel=1;k>f[num][bel];bel++){
		k-=f[num][bel];
	}
	int pos;
	for(pos=(bel-1)*Maxs+1;k>(cnt[pos]==num);pos++){
		k-=(cnt[pos]==num);
	}
	return pos;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	scanf("%d",&m);
	sum[0]=s_block[0]=n;
	for(int i=1;i<=n;i++){
		f[0][(a[i]-1)/Maxs+1]++;
	}
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&qu[i].l,&qu[i].r,&qu[i].k);
		qu[i].id=i;
	}
	sort(qu+1,qu+1+m);
	int pos_l=1,pos_r=0;
	for(int i=1;i<=m;i++){
		if(qu[i].k>n){
			ans[qu[i].id]=-1;
			continue;
		}
		while(pos_r<qu[i].r){
			pos_r++;
			add(a[pos_r],1);
		}
		while(pos_l>qu[i].l){
			pos_l--;
			add(a[pos_l],1);
		}
		while(pos_r>qu[i].r){
			add(a[pos_r],-1);
			pos_r--;
		}
		while(pos_l<qu[i].l){
			add(a[pos_l],-1);
			pos_l++;
		}
		ans[qu[i].id]=query(qu[i].k);
		if(cnt[ans[qu[i].id]]==0){
			ans[qu[i].id]=-1;
		}
	}
	for(int i=1;i<=m;i++){
		if(ans[i]==-1){
			puts("0");
		}
		else{
			printf("%d\n",ans[i]);
		}
	}
	return 0;
}
posted @ 2020-11-18 23:23  with_hope  阅读(121)  评论(0编辑  收藏  举报