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\n",ans);
71         }
72     }
73 }

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

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

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

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

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

posted @ 2018-01-13 22:50  JZYshuraK_彧  阅读(204)  评论(0编辑  收藏  举报