LOJ6285.数列分块入门 9(分块在线求区间众数)

link

思路:

考虑对于查询的区间来说众数出现的几种可能:

  • 左端点 q l ql ql所在块的数
  • 右端点 q r qr qr所在块的数
  • b e l o n g [ q l ] + 1 到 b e l o n g [ q r ] − 1 belong[ql]+1到belong[qr]-1 belong[ql]+1belong[qr]1的块的众数

对于每一种可能,我们都计算出他出现的次数,最后取出现次数最多并且值最小的就可以。

  • 如何求某个数在某段区间的出现次数呢?大概有两种做法,一是前缀和,二是二分;对于每一个数都用 v e c t o r vector vector存储一下他出现过的位置,每次用 u p p e r _ b o u n d − l o w e r _ b o u n d upper\_bound - lower\_bound upper_boundlower_bound就是答案。
  • 由于 a i a_i ai的范围过大,所以需要离散化一波~
  • 对于前两种可能,我们可以直接枚举出现的每个数,复杂度为 O ( 2 n ) O(2\sqrt n) O(2n );
  • 第三种可能,直接枚举显然不现实。预处理出 d p [ i ] [ j ] dp[i][j] dp[i][j]表示第 i i i块到第 j j j块的众数。
for(int i=1;i<=num;i++){
		int res=0,cnt=0;
		memset(mp,0,sizeof mp);
		for(int j=(i-1)*block+1;j<=n;j++){
			mp[a[j]]++;
			if(mp[a[j]]>cnt) cnt=mp[a[j]],res=a[j];
			else if(mp[a[j]]==cnt&&a[j]<res) res=a[j];
			dp[i][belong[j]]=res;
		}
	}

枚举 i i i表示起始块的编号, j j j表示后面的数,递推过去就好了。时间复杂度为 O ( n n ) O(n\sqrt n) O(nn )

  • 对于查询的时候,枚举每一个可能的数并且计算出现的次数,取最大值就好了。
  • 要注意,离散化后的下标关系,容易搞混。

代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll>PLL;
typedef pair<int, int>PII;
typedef pair<double, double>PDD;
#define I_int ll
inline ll read(){ll x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-')f = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}return x * f;}

inline void write(ll x){if (x < 0) x = ~x + 1, putchar('-');if (x > 9) write(x / 10);putchar(x % 10 + '0');}

#define read read()
#define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define multiCase int T;cin>>T;for(int t=1;t<=T;t++)
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define perr(i,a,b) for(int i=(a);i>(b);i--)

ll ksm(ll a, ll b,ll mod){ll res = 1;while(b){if(b&1)res=res*a%mod;a=a*a%mod;b>>=1;}return res;}

const int maxn=100000+7,maxm=2e5+7,inf=0x3f3f3f3f;

int a[maxn],b[maxn],n,block,m,l[5100],r[5100],belong[maxn];
vector<int>g[maxn];
int dp[5100][5100],mp[maxn];
int Find(int x, int l, int r) {
    return upper_bound(g[x].begin(), g[x].end(), r) - lower_bound(g[x].begin(), g[x].end(), l);
}
int query(int ql,int qr){
	int res=inf,cnt=0;
	if(belong[ql]==belong[qr]){
		rep(i,ql,qr){
			int tmp=Find(a[i],ql,qr);
			if(cnt<tmp) cnt=tmp,res=a[i];
			else if(cnt==tmp&&a[i]<res) res=a[i];
		}
		return res;
	}
	res=dp[belong[ql]+1][belong[qr]-1];
	cnt=Find(res,ql,qr);
	rep(i,ql,r[belong[ql]]){
		int tmp=Find(a[i],ql,qr);
		if(cnt<tmp) cnt=tmp,res=a[i];
		else if(cnt==tmp&&a[i]<res) res=a[i];
	}
	rep(i,l[belong[qr]],qr){
		int tmp=Find(a[i],ql,qr);
		if(cnt<tmp) cnt=tmp,res=a[i];
		else if(cnt==tmp&&a[i]<res) res=a[i];
	}
	return res;
}

int main(){
	n=read;
	block=150;
	int num=n/block;
	if(n%block) num++;
	
	rep(i,1,num) l[i]=(i-1)*block+1,r[i]=i*block;
	
	rep(i,1,n) a[i]=read,b[i]=a[i];
	
	sort(b+1,b+1+n);
	
	m=unique(b+1,b+1+n)-(b);
	
	rep(i,1,n){
		a[i]=lower_bound(b+1,b+m,a[i])-(b);
		g[a[i]].push_back(i);
		belong[i]=(i-1)/block+1;
	}

	for(int i=1;i<=num;i++){
		int res=0,cnt=0;
		memset(mp,0,sizeof mp);
		for(int j=(i-1)*block+1;j<=n;j++){
			mp[a[j]]++;
			if(mp[a[j]]>cnt) cnt=mp[a[j]],res=a[j];
			else if(mp[a[j]]==cnt&&a[j]<res) res=a[j];
			dp[i][belong[j]]=res;
		}
	}
	
	
	rep(i,1,n){
		int ql=read,qr=read;
		write(b[query(ql,qr)]);
		puts("");
	}
	
	
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

豆沙睡不醒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值