zoukankan      html  css  js  c++  java
  • 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、 如果不相同,则以一般情况来讨论。见下图。

    因为序列为非递减序列,因此值相同的数字必然连续出现。将区间分为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
    ", 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
    ",tem);
    65                 else printf("%d
    ", 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
    ", 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
    ",tem);
    57                 else printf("%d
    ", max(tem, rmq(right[a] + 1, left[b] - 1)));
    58             }
    59         }
    60     }
    61     return 0;
    62 }
    View Code
  • 相关阅读:
    负数求余数 C 和 Matlab&Python 处理不一样
    [Matlab] 线性卷积&圆周卷积代码实现
    [Arduino] 驱动RC522 读取 UID例程
    [C++] Nested Radical Constant
    [Arduino] 学习总结小合集(更新ING)
    谐振电路的品质因素总结
    142. Linked List Cycle II
    664. Strange Printer
    188. Best Time to Buy and Sell Stock IV
    50. Pow(x, n)
  • 原文地址:https://www.cnblogs.com/fenshen371/p/3223040.html
Copyright © 2011-2022 走看看