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一定要分开读,不然是没有办法退出的。因为那个特判是下一条语句,对于一个没有完成的读入是没有意义的。
| 欢迎来原网站坐坐! >原文链接<