分块9:求区间众数(若存在多个众数,输出值最小的)

本文介绍了一种在线处理区间众数查询的高效算法,针对序列中蒲公英种类编号的查询需求,采用分块技术预处理数据,实现快速响应多次区间众数查询。文章详细解释了算法原理,包括如何通过维护块内众数和次数来优化查询效率,以及如何处理与前次查询结果相关的强制在线查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

警戒:以后类似于没看到强制在线这种傻逼错误千万不要犯啊。

 

 

问题描述:

在乡下的小路旁种着许多蒲公英,而我们的问题正是与这些蒲公英有关。

为了简化起见,我们把所有的蒲公英看成一个长度为n的序列 (a_1,a_2..a_n)(a1​,a2​..an​) ,其中 a_iai​ 为一个正整数,表示第i棵蒲公英的种类编号。

而每次询问一个区间 [l,r],你需要回答区间里出现次数最多的是哪种蒲公英,如果有若干种蒲公英出现次数相同,则输出种类编号最小的那个。

注意,你的算法必须是在线的

输入输出格式

输入格式:

 

第一行两个整数 n,m ,表示有n株蒲公英,m 次询问。

接下来一行n个空格分隔的整数 a_iai​ ,表示蒲公英的种类

再接下来m 行每行两个整数 l_0,r_0l0​,r0​ ,我们令上次询问的结果为 x(如果这是第一次询问, 则 x=0)。

令 l=(l_0+x-1)\bmod n + 1,r=(r_0+x-1) \bmod n + 1l=(l0​+x−1)modn+1,r=(r0​+x−1)modn+1 ,如果 l>r,则交换 l,r 。

最终的询问区间为[l,r]。

 

输出格式:

 

输出m 行。每行一个整数,表示每次询问的结果。

 

 

分析:

   从之前用分块代替树状数组求区间和的例子我们可以知道,分块是把一个区间分解成若干个小的区间,先通过预处理求出这些小区间的一些值,通过空间换时间达成时空平衡。

   代替树状数组的思路很简单,我们把n个数分成T个区间,然后对每个区间求出区间和,并且开一个数组add,存储整个区间需要加的值。如果我们需要对整个区间进行修改,那么把区间所包含的完整的块儿,直接在add数组里加上一个值,对不完整的块儿,我们直接暴利枚举,在a数组的基础上加上所要加的值。假如说,我们的块儿的长度为根号n,那么一次区间加上一个数的操作,复杂度就是O(sqrt(n)),当然查询的时候也是O(sqrt(n))了。

    但是这道题我们求得是区间众数,区间众数是不可以相加减的。我们考虑,如果我们已经知道了一个块儿的众数(这个块儿的大小先不说),并且知道了这个块儿所代表的区间内的所有数出现的次数,那么当我们查询l,r之间的众数时,这个众数无非两种情况,一个块儿内的数,一个是块儿外的小范围的数。我们做的就是,每一个块儿带一个数组存储出现的数的次数,然后暴利枚举小范围,加到块儿的数组里面,然后求出众数,之后再回复原状。为了维护我们的信息,假如说我们把n分成t个块儿,我们不仅维护这t个块儿,还要维护这些块儿组成的大块儿。

   那么我们预处理的复杂度是n*t*t,每次询问的复杂度是n/t,所有的询问就是m*n/t。我们让这两个复杂度相等,发现此时t≈n的立方根。整个算法是可以A掉题的。

   然后最傻逼的就是,我这道题上午就写出了板子,一道板子题,花了一天的时间。因为我没看到题目输出要求,这个是强制在线题,也就是答案与上一个有关系!

   哎,心累。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
using namespace std;
int p,q,t,n,m,l,r,L,lan,pos,most[1600],maxn[1600],sum[1600][41000],a[41000],b[40],temp[41000],c[41000];
void discrete()
{
	sort(c+1,c+n+1);
	int sz=unique(c+1,c+n+1)-c-1;
	for(int i=1;i<=n;i++) a[i]=lower_bound(c+1,c+sz+1,a[i])-c;
}
int ask(int l,int r)
{
	l=(l+lan-1)%n+1;
	r=(r+lan-1)%n+1;
	if(l>r) swap(l,r);
	p=l/L;q=r/L;
	if(l%L==1)p++;
	else if(l%L) p+=2;
	else p++;
	int ans;
	if(q-p<0){
		int maxx=0;
		for(int i=l;i<=r;i++){ 
			temp[a[i]]++;
			if(temp[a[i]]>maxx)
				maxx=temp[a[i]],ans=a[i];
			if(temp[a[i]]==maxx&&c[a[i]]<c[ans])
				ans=a[i];
		}
		for(int i=l;i<=r;i++) temp[a[i]]--;
		return c[ans];
	}else{
		pos=b[p-1]+q-p+1;int maxx=maxn[pos];ans=most[pos];
		for(int i=l;i<=L*(p-1);i++){
			sum[pos][a[i]]++;
			if(sum[pos][a[i]]>maxx)
				maxx=sum[pos][a[i]],ans=a[i];
			if(sum[pos][a[i]]==maxx&&c[a[i]]<c[ans])
				ans=a[i];
		}
		for(int i=L*q+1;i<=r;i++){
			sum[pos][a[i]]++;
			if(sum[pos][a[i]]>maxx)
				maxx=sum[pos][a[i]],ans=a[i];
			if(sum[pos][a[i]]==maxx&&c[a[i]]<c[ans])
				ans=a[i];
		}
		for(int i=l;i<=L*(p-1);i++) sum[pos][a[i]]--;
		for(int i=L*q+1;i<=r;i++) sum[pos][a[i]]--;
		return c[ans] ;
	}
}
int main()
{
	//freopen("kgx.in","r",stdin);
	//freopen("kgx.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),c[i]=a[i];
	discrete();
	t=pow(1.0*n,1.0/3.0);L=n/t;
	for(int i=1;i<=t;i++) b[i]=b[i-1],b[i]+=t-i+1;
	for(int i=1;i<=t;i++)
		for(int j=1;j<=t-i+1;j++){
			pos=b[i-1]+j;
			int maxx=0;int ans;
			for(int k=L*(i-1)+1;k<=L*(i+j-1);k++){
				sum[pos][a[k]]++;
				if(sum[pos][a[k]]>maxx)
					maxx=sum[pos][a[k]],ans=a[k];
				if(sum[pos][a[k]]==maxx&&c[a[k]]<c[ans])
				ans=a[k];
			}
			most[pos]=ans;maxn[pos]=maxx;
		}
	for(int i=1;i<=m;i++){
		scanf("%d%d",&l,&r);
		printf("%d\n",lan=ask(l,r));
	}
	return 0;
}

 

``` #include <bits/stdc++.h> using namespace std; const int MAXN = 40005; int p[MAXN], aid[MAXN], n, m, t, block_size; vector<int> b[1005], c[1005]; vector<int> all_values; void add(int id, int block) { for (int i = 0; i < b[block].size(); ++i) { if (b[block][i] == id) { c[block][i]++; return; } } b[block].push_back(id); c[block].push_back(1); } int count(int l, int r) { int u = p[l], v = p[r]; static vector<int> freq(MAXN, 0); vector<int> used_ids; int current_max = 0, current_id = 0; auto update = [&](int id, int cnt) { if (freq[id] == 0) used_ids.push_back(id); freq[id] += cnt; if (freq[id] > current_max || (freq[id] == current_max && id < current_id)) { current_max = freq[id]; current_id = id; } }; if (u == v) { for (int i = l; i <= r; ++i) update(aid[i], 1); } else { for (int i = l; i <= u * block_size; ++i) update(aid[i], 1); for (int i = (v-1)*block_size+1; i <= r; ++i) update(aid[i], 1); for (int block = u+1; block < v; ++block) for (int j = 0; j < b[block].size(); ++j) update(b[block][j], c[block][j]); } for (int id : used_ids) freq[id] = 0; return all_values[current_id - 1]; } int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n >> t; block_size = sqrt(n) + 1; vector<int> a(n+1); for (int i = 1; i <= n; ++i) { cin >> a[i]; all_values.push_back(a[i]); } sort(all_values.begin(), all_values.end()); all_values.erase(unique(all_values.begin(), all_values.end()), all_values.end()); for (int i = 1; i <= n; ++i) aid[i] = lower_bound(all_values.begin(), all_values.end(), a[i]) - all_values.begin() + 1; for (int i = 1; i <= n; ++i) { p[i] = (i-1)/block_size + 1; add(aid[i], p[i]); } int ans = 0; while (t--) { int L, R, l, r; cin >> L >> R; l = (L + ans - 1) % n + 1; r = (R + ans - 1) % n + 1; if (l > r) swap(l, r); ans = count(l, r); cout << ans << &#39;\n&#39;; } return 0; }```帮我继续优化该代码使得该代码可以通过该题,并给出时间复杂度:给一个长度是n的数组,a[1],a[2],a[3],...,a[n-1],a[n], 然后有m次询问,每次询问给一个区间[x,y], 问区间众数是多少,如果区间多个众数出现的次数相同,输出最小的那个 一下定义什么是众数众数即为在给定序列中出现次数最多的数 输入格式 第一行有两个整数,分别表示a数组的长度 n 和询问次数 m。 第二行有 n 个整数,第 i 个整数表示a[i] 接下来 m 行,每行两个整数 L, R,表示一次询问。输入是加密的,解密方法如下: 令上次询问的结果为 x(如果这是第一次询问,则 x = 0),设 l=((L+x-1)mod n) + 1,r=((R+x-1) mod n) + 1。如果 l > r,则交换 l, r。 最终的询问区间为计算后的 [l, r]。 部分数据,保证 n,m <= 3000。 100% 的数据,保证 1<= n <= 40000,1<= m <= 50000,1<= a[i] <= 10^9,1 <= L,R<= n。 输出格式 对于每次询问,输出一行一个整数表示答案。 输入/输出例子1 输入: 6 3 1 2 3 2 1 2 1 5 3 6 1 5 输出: 1 2 1
03-20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值