POJ 3368 Frequent values 线段树与RMQ解法

题意:给出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

================================

rmq st算法求解代码

 1 #include<stdio.h>

 2 #include<math.h>

 3 #include<algorithm>

 4 #define maxn 100020

 5 using namespace std;

 6 

 7 int num[maxn], left[maxn], right[maxn], tmax[maxn][33];

 8 void st(int n)

 9 {

10     int k = (int)(log((double)n) / log(2.0));

11     for (int i = 0; i < n; i++)

12         tmax[i][0] = right[i] - left[i] + 1;//递推的初值

13     for (int j = 1; j <= k; j++)

14         for (int i = 0; i + (1 << j) - 1 < n; i++)

15         {

16             int m = i + (1 << (j - 1));//求出中间值

17             tmax[i][j] = max(tmax[i][j-1], tmax[m][j-1]);

18         }

19 }

20 //查询i和j之间的最值,注意i是从0开始的

21 int rmq(int i, int j)

22 {

23     int k = (int)(log(double(j - i + 1)) / log(2.0));

24     int t1 = max(tmax[i][k], tmax[j-(1<<k)+1][k]);

25     return t1;

26 }

27 int main()

28 {

29     int n, q;

30     //freopen("data.in", "r", stdin);

31     while (~scanf("%d",&n) && n)

32     {

33         scanf("%d",&q);

34         for (int i = 0; i < n; i++)

35         {

36             scanf("%d",&num[i]);

37             if (!i || num[i] != num[i-1]) left[i] = i;

38             else left[i] = left[i-1];

39         }

40         for (int i = n - 1; i > -1; i--)

41         {

42             if (i == (n - 1) ||num[i] != num[i+1])

43                 right[i] = i;

44             else right[i] = right[i+1];

45         }

46         st(n);

47         while (q--)

48         {

49             int a, b;

50             scanf("%d%d",&a,&b);

51             a--; b--;

52             if (num[b] == num[a]) printf("%d\n", b - a + 1);

53             else

54             {

55                 int tem = max(right[a] - a + 1, b - left[b] + 1);

56                 if (right[a] + 1 > left[b] - 1) printf("%d\n",tem);

57                 else printf("%d\n", max(tem, rmq(right[a] + 1, left[b] - 1)));

58             }

59         }

60     }

61     return 0;

62 }
View Code

 

你可能感兴趣的:(value)