POJ3368 Frequent values(RMQ线段树)
题目大概说给一个递增序列,询问区间出现最多的数。
用莫队算法比较直观,虽然应该会T。。好像也可以主席树。。不过题目给的序列是有序的,因而相同的数会聚在一起。
考虑把序列分成一段一段,使每段都包含极大的相同的数字
这样对于每一个区间查询:
-
- 可能这个区间左边或右边没有包含完整的一段,而其长度在段里对左或右端点进行二分查找就知道了
- 而除去两边不完整的,还要求出中间若干完整段的最大长度,这个就是用RMQ来快速解决了
于是这题就能这样解决了。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 #define MAXN 222222 6 7 int lrec[MAXN],rrec[MAXN]; 8 9 int tree[MAXN<<2],N,x,y; 10 void update(int i,int j,int k){ 11 if(i==j){ 12 tree[k]=y; 13 return; 14 } 15 int mid=i+j>>1; 16 if(x<=mid) update(i,mid,k<<1); 17 else update(mid+1,j,k<<1|1); 18 tree[k]=max(tree[k<<1],tree[k<<1|1]); 19 } 20 int query(int i,int j,int k){ 21 if(x>y) return 0; 22 if(x<=i && j<=y){ 23 return tree[k]; 24 } 25 int mid=i+j>>1,res=0; 26 if(x<=mid) res=max(res,query(i,mid,k<<1)); 27 if(y>mid) res=max(res,query(mid+1,j,k<<1|1)); 28 return res; 29 } 30 31 int seq[MAXN]; 32 int main(){ 33 int n,q,a,b; 34 while(~scanf("%d",&n) && n){ 35 scanf("%d",&q); 36 for(int i=1; i<=n; ++i){ 37 scanf("%d",seq+i); 38 } 39 seq[n+1]=111111; 40 41 int rn=0; 42 lrec[0]=1; 43 for(int i=1; i<=n+1; ++i){ 44 if(seq[i]!=seq[i+1]){ 45 rrec[rn]=i; 46 rn++; 47 lrec[rn]=i+1; 48 } 49 } 50 51 memset(tree,0,sizeof(tree)); 52 for(N=1; N<rn; N<<=1); 53 for(int i=0; i<rn; ++i){ 54 x=i; y=rrec[i]-lrec[i]+1; 55 update(0,N-1,1); 56 } 57 58 int i,j,res; 59 while(q--){ 60 scanf("%d%d",&a,&b); 61 i=lower_bound(rrec,rrec+rn,a)-rrec; 62 j=upper_bound(lrec,lrec+rn,b)-lrec-1; 63 if(rrec[i]>=b){ 64 printf("%d\n",b-a+1); 65 continue; 66 } 67 res=max(rrec[i]-a+1,b-lrec[j]+1); 68 x=i+1; y=j-1; 69 res=max(res,query(0,N-1,1)); 70 printf("%d\n",res); 71 } 72 } 73 return 0; 74 }