题意:给出n个数的非递减序列,进行q次查询。每次查询给出两个数a,b,求出第a个数到第b个数之间数字的最大频数。

如序列:-1 -1 1 1 1 1 2 2 3

第2个数到第5个数之间出现次数最多的是数字1,它的频数3。

思路:假设查询时的参数为a, b。这道题查询时有以下两种情况:

1、 num[a] = num[b]. 即区间内的数字全相同,此时答案为b - a + 1。

2、 如果不相同,则以一般情况来讨论。见下图。

POJ 3368 Frequent values 线段树与RMQ解法

 

因为序列为非递减序列,因此值相同的数字必然连续出现。将区间分为3部分。num[a]以及与它值相同的区域构成第一部分,num[b]以及与它值相同的区域构成第三部分。区间[a, b]中剩下的构成第二部分。

定义left[i]表示与num[i]值相等的数字从左起开始的下标,right[i]表示与num[i]值相等的数字从右起开始的下标。

由图易知,第二部分里的数字,left与right值均在区间[a,b]内。

当给出区间范围a,b后,第一部分在区间内出现的次数为right[a] - a + 1。第三部分在区间内出现的次数为b - left[b] + 1。

如果right[a] + 1 > left[b] - 1,说明区间没有第二部分,直接输出上面两个值中的较大者。

如果存在第二部分,需要求出第二部分里的最大频数。不过这次就非常好求了,因为所有的数开始和结束都是在第二部分中,不存在部分出现的情况。定义tmax[i] = right[i] - left[i] + 1。则第二部分里数字的最大出现次数,即为该区间内tmax的最大值。将该值求出后与前面一三部分求出的较大者比较,最大的值即为最终答案。

因为查询量巨大,当第二部分需要计算时,可以采用线段树或者rmq。

现将两种方法的代码都给出。根据提交的结果来看,线段树所需空间远小于rmq,且速度稍快一点(不排除服务器的偶然性以及我rmq代码的效率比较低等原因)。

线段树求解代码

 1 #include<stdio.h>
 2 #include<algorithm>
 3 #define lson l, m, rt << 1
 4 #define rson m + 1, r, rt << 1 | 1
 5 #define maxn 100020
 6 #define inf 0x3f3f3f3f
 7 using namespace std;
 8 
 9 int num[maxn], left[maxn], right[maxn], tmax[maxn<<2];
10 void PushUp(int rt)
11 {
12     tmax[rt] = max(tmax[rt<<1], tmax[rt<<1|1]);
13 }
14 void build(int l,int r,int rt)
15 {
16     if (l == r)
17     {
18         tmax[rt] = right[l] - left[l] + 1;
19         return;
20     }
21     int m = (l + r) >> 1;
22     build(lson);
23     build(rson);
24     PushUp(rt);
25 }
26 int query(int L,int R,int l,int r,int rt)
27 {
28     if (L <= l && r <= R) return tmax[rt];
29     int m = (l + r) >> 1;
30     int ret = -inf;
31     if (L <= m) ret = max(ret, query(L, R, lson));
32     if (m < R) ret = max(ret, query(L, R, rson));
33     return ret;
34 }
35 int main()
36 {
37     int n, q;
38     //freopen("data.in", "r", stdin);
39     while (~scanf("%d",&n) && n)
40     {
41         scanf("%d",&q);
42         for (int i = 0; i < n; i++)
43         {
44             scanf("%d",&num[i]);
45             if (!i || num[i] != num[i-1]) left[i] = i;
46             else left[i] = left[i-1];
47         }
48         for (int i = n - 1; i > -1; i--)
49         {
50             if (i == (n - 1) ||num[i] != num[i+1])
51                 right[i] = i;
52             else right[i] = right[i+1];
53         }
54         build(0, n - 1, 1);
55         while (q--)
56         {
57             int a, b;
58             scanf("%d%d",&a,&b);
59             a--; b--;
60             if (num[b] == num[a]) printf("%d\n", b - a + 1);
61             else
62             {
63                 int tem = max(right[a] - a + 1, b - left[b] + 1);
64                 if (right[a] + 1 > left[b] - 1) printf("%d\n",tem);
65                 else printf("%d\n", max(tem, query(right[a] + 1, left[b] - 1, 0, n - 1, 1)));
66             }
67         }
68     }
69     return 0;
70 }
View Code

相关文章: