UVA-11235___Frequent values——RMQ

题目链接:点我啊点我啊

题目大意:

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

解题思路:

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

代码思路:

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

若询问区间 [ L , R ] [L, R]
L L 个数字在 [ L , R ] [L, R] 内出现的次数为 r i g h [ L ] L + 1 righ[L]-L+1
R R 个数字在 [ L , R ] [L, R] 内出现的次数为 R l e f [ R ] + 1 R-lef[R]+1
中间第 n u m [ L ] + 1 num[L]+1 段到第 n u m [ R ] 1 num[R]-1 段的 c n t 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 = 0;
	while ((1<<(k+1)) <= r-l+1) k++;
	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);

			}
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Scar_Halo/article/details/82955066