UVA-11235___Frequent values —— RMQ

题目链接:点我啊点我啊

题目大意:

    给出一个长度为 n n n的非降序整数数列,有 q q q个问题,每次询问区间 [ l , r ] [l, r] [l,r],问在这个区间内,出现次数最多的那个数的次数是多少???

解题思路:

    很明显是RMQ,但问题是这个题目询问的是出现最多的数的出现次数,那么我们就将RMQ的初始值设为每个数的出现次数,再用RMQ即可

代码思路:

    因为我们初始的是每个数的出现次数,而且题目给的是非降序数列,所以我们对每个数所在的那一段连续相同的一段给出一些定义:
l e n — — len—— len第几段数字
c n t [ i ] — — cnt[i]—— cnt[i] i i i段数字出现了多少次
n u m [ i ] — — num[i]—— num[i] i i i个数字处在第几段
l e f [ i ] — — lef[i]—— lef[i] i i i段数字的起始位置
r i g h [ i ] — — righ[i]—— righ[i] i i i段数字的末尾位置

若询问区间 [ L , R ] [L, R] [L,R]
L L L个数字在 [ L , R ] [L, R] [L,R]内出现的次数为 r i g h [ L ] − L + 1 righ[L]-L+1 righ[L]L+1
R R R个数字在 [ L , R ] [L, R] [L,R]内出现的次数为 R − l e f [ R ] + 1 R-lef[R]+1 Rlef[R]+1
中间第 n u m [ L ] + 1 num[L]+1 num[L]+1段到第 n u m [ R ] − 1 num[R]-1 num[R]1段的 c n t cnt cnt的最大值就是RMQ

核心:灵活运用RMQ,在本题中就将每个数出现的次数作为RMQ的dp初始值

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int q, len, a[N];
int cnt[N], num[N], lef[N], righ[N];
int dp[N][50];

void ST(int n, int d[]) {
	for (int i=1; i<=n; i++) dp[i][0] = d[i];
	for (int j=1; (1<<j) <= n; j++) {
		for (int i=1; i+(1<<j)-1 <= n; i++) {
			dp[i][j] = max(dp[i][j-1], dp[i + (1<<(j-1))][j-1]);
		}
	}
}
int RMQ(int l, int r) {
	int k = log(r - l + 1.0) / log(2.0); 
	return max(dp[l][k], dp[r - (1<<k)+1][k]);
}

int main() {
	int n;
	while(~scanf("%d", &n), n) {
		memset(cnt, 0, sizeof(cnt));
		scanf("%d%d", &q, &a[1]);
		len = 1;
		lef[len] = 1;
		num[1] = len;
		cnt[len] = 1;
		for(int i=2; i<=n; i++) {
			scanf("%d", &a[i]);
			if(a[i] == a[i-1]) {
				num[i] = len;
				cnt[len]++;
			} else {
				righ[len] = i-1;
				len++;
				cnt[len] = 1;
				lef[len] = i;
				num[i] = len;
			}
		}
		ST(len, cnt);

		while(q--) {
			int l, r, ans;
			scanf("%d%d", &l, &r);
			if(num[l] == num[r]) printf("%d\n", r-l+1);
			else {
				ans = 0;
				if(num[l]+1 <= num[r]-1)
					ans = RMQ(num[l]+1, num[r]-1);
				ans = max(ans, max(righ[num[l]]-l+1, r-lef[num[r]]+1));
				printf("%d\n", ans);
			}
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值