zoukankan      html  css  js  c++  java
  • Frequent Value

    Frequent Value poj-3368

        题目大意:给你n个数的数列,保证它是单调递增的。给你m个询问,每个询问是询问两个节点之间最长的连续的相等的数的长度。

        注释:n,m<=100000。

          想法:这道题是我做的第一道有点儿意思的RMQ(RMQ?猛戳)的题。刚学RMQ,就把这道题更出来了。我们仍然采用ST算法。我们思考,f[i][j]表示从a[i]开始的$2^j$数中,最长的相等的长度,那么我们思考,这东西如何更新。首先,我们想到,可以有它的中点分成的两个区间分别更新,这样的更新答案显然是对的,但是一定是最大的吗?一定不是的,我们很自然的想起一道题——小白逛公园,一道挺好玩儿的题。这道题同理,我们有这样的更新答案的方式,就是那个最长的区间包括了中间的两个端点,这样的话,我们就可以更新答案。在记录时,我们需要用到两个数组。分别是l[i]和r[i],分别表示从a[i]开始的向左的最后一个和a[i]相等的数,r[]同理。那么,我们就可以通过这两个数组更新答案。这时,我们需要注意两个事情。

            1.一方面,我们发现,如果l数组小于i这是不合理的,我们需要对它取一个max。右边的r[]同理。

            2.另一方面,我们有一个必要条件:中间的两个数必须相等,必须相等,必须相等!!!虽然poj的数据并没有针对性的卡掉这个点,但是这是不对的!!

          最后,我们说一下查询,查询时我们依然需要注意中间点的覆盖,和预处理时同理,在此不细谈了。

        最后,附上丑陋的代码......

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #define N 100010
     5 using namespace std;
     6 int l[N],r[N],a[N],log[N],f[N][18];
     7 int main()
     8 {
     9     int ll,rr,lmax,rmin;
    10     int n,m;
    11     int ans;
    12     int x,y;
    13     int len;
    14     for(int i=2;i<=N;i++) log[i]=log[i>>1]+1;//这东西可以在外面预处理 
    15     while(1)
    16     {
    17         scanf("%d",&n);
    18         if(n==0) return 0;
    19         scanf("%d",&m);
    20         memset(f,0,sizeof(f));
    21         for(int i=1;i<=n;i++)
    22         {
    23             scanf("%d",&a[i]);
    24         }
    25         // for(int i=2;i<=n;i++) log[i]=log[i>>1]+1;
    26         l[1]=1;//这是开始处理l和r数组 
    27         for(int i=2;i<=n;i++)
    28         {
    29             if(a[i]==a[i-1]) l[i]=l[i-1];
    30             else l[i]=i;
    31         }
    32         r[n]=n;
    33         for(int i=n-1;i>=1;i--)
    34         {
    35             if(a[i]==a[i+1]) r[i]=r[i+1];
    36             else r[i]=i;
    37         }
    38         for(int i=1;i<=n;i++) f[i][0]=1;
    39         for(int i=1;(1<<i)<=n;i++)
    40         {
    41             for(int j=1;j+(1<<i)-1<=n;j++)
    42             {
    43                 f[j][i]=max(f[j][i-1],f[j+(1<<(i-1))][i-1]);
    44                 if(a[j+(1<<(i-1))]!=a[j+(1<<(i-1))-1]) continue;//很重要很重要很重要 
    45                 lmax=max(j,l[j+(1<<(i-1))-1]);
    46                 rmin=min(j+(1<<i)-1,r[j+(1<<(i-1))]);
    47                 f[j][i]=max(f[j][i],rmin-lmax+1);
    48             }
    49         }
    50         /*for(int i=1;i<=n;i++)
    51         {
    52             for(int j=1;j<=4;j++)
    53             {
    54                 cout<<"f["<<i<<"]["<<(1<<j)<<"]="<<f[i][j]<<endl;
    55             }
    56         }*/
    57         for(int i=1;i<=m;i++)
    58         {
    59             ans=0;
    60             scanf("%d%d",&x,&y);
    61             len=log[y-x+1];
    62             ans=max(f[x][len],f[y-(1<<len)+1][len]);
    63             // cout<<"     "<<a[x+(1<<len)-1]<<" "<<a[y-(1<<len)+1]<<endl;
    64             if(a[x+(1<<len)-1]==a[y-(1<<len)+1])//同样的重要 
    65             {
    66                 rr=min(r[y-(1<<len)+1],y);
    67                 ll=max(l[x+(1<<len)-1],x);
    68                 ans=max(ans,rr-ll+1);
    69             }
    70             printf("%d
    ",ans);
    71         }
    72     }
    73 }

        小结:RMQ中的ST算法是一个很好的思想,也是我第一次接触到倍增的问题,说一下容易错误的点。

          错误:1.在预处理时,那个 i 和 j 比较容易搞混,别问我是怎么知道的......

             2.对于端点的处理一定要细心,这个和平常的ST不太一样,对于端点的拿捏也是比较的注重的。

             3.在最后加上多组数据后,return 0一定要写到外面

             4.这道题的退出条件很有意思,我们发现,n和m一定要分开读,不然是没有办法退出的。因为那个特判是下一条语句,对于一个没有完成的读入是没有意义的。

  • 相关阅读:
    uitableview 默认选中行
    ipad 开发常用问题
    NSDate常用代码范例
    MOSS2010站点大文件上传设置
    scm xcode 配置
    ipad 开发 遇到BadAccess
    Tutorial: iPhone SQLite Encryption With SQLCipher
    uitableview
    UML建模——活动图(Activity Diagram)
    【随感】.........................
  • 原文地址:https://www.cnblogs.com/ShuraK/p/8280792.html
Copyright © 2011-2022 走看看