RMQ:
有一个不变的数组,不停的求一个区间的最小值。
使用倍增的思想优化到logN;
d(i,j) 表示从 i 开始的,长度为2j的一段元素中的最小值。
那么状态转移方程:
d(i,j) = min{ d(i,j-1) , d(i+2j-1,j-1) }
题目链接:https://vjudge.net/contest/146667#problem/B
题意:一个非降序的数组,询问(i,j)中出现次数最多的数值,所对应的出现次数是多少。
分析:
进行游戏编码,value[i] 和count[i] 表示第 i 段对应的数值和出现的次数,
num[i] 表示位置 i 对应的段所在的编号,left[i] 表示 i 位置所在的段的左端点,right[i] 右端点。
那么(L,R)就是分成3个部分。
一个是right[L] - L + 1,R-left[R] + 1,还一个就是RMQ(count,num[L]+1,num[R]-1)。
特殊情况:如果L和R在同一段里面,R-L+1;
Source Code:

1 #include<cstdio> 2 #include<algorithm> 3 #include<vector> 4 using namespace std; 5 6 const int maxn = 100000 + 5; 7 const int maxlog = 20; 8 9 // 区间最*大*值 10 struct RMQ 11 { 12 int d[maxn][maxlog]; 13 void init(const vector<int>& A) 14 { 15 int n = A.size(); 16 for(int i = 0; i < n; i++) d[i][0] = A[i]; 17 for(int j = 1; (1<<j) <= n; j++) 18 for(int i = 0; i + (1<<j) - 1 < n; i++) 19 d[i][j] = max(d[i][j-1], d[i + (1<<(j-1))][j-1]); 20 } 21 22 int query(int L, int R) 23 { 24 int k = 0; 25 while((1<<(k+1)) <= R-L+1) k++; // 如果2^(k+1)<=R-L+1,那么k还可以加1 26 return max(d[L][k], d[R-(1<<k)+1][k]); 27 } 28 }; 29 30 int a[maxn], num[maxn], left[maxn], right[maxn]; 31 RMQ rmq; 32 33 34 int main() 35 { 36 int n,q; 37 while(scanf("%d%d",&n,&q)==2) 38 { 39 for(int i=0; i<n; i++) 40 scanf("%d",&a[i]); 41 42 a[n] = a[n-1] + 1; 43 int start = -1; 44 vector<int> count; 45 for(int i=0; i<=n; i++) 46 { 47 if(i==0||a[i]>a[i-1]) 48 { 49 if(i>0) 50 { 51 count.push_back(i-start); 52 for(int j=start;j<i;j++) { 53 num[j] = count.size()-1; 54 left[j] = start; 55 right[j] = i-1; 56 } 57 } 58 start = i; 59 } 60 } 61 62 rmq.init(count); 63 while(q--) { 64 int L,R,ans; 65 scanf("%d%d",&L,&R); 66 L--; 67 R--; 68 if(num[L]==num[R]) ans = R - L + 1; 69 else { 70 ans = max(R-left[R]+1,right[L]-L+1); 71 if(num[L]+1<num[R]) 72 ans = max(ans,rmq.query(num[L]+1,num[R]-1)); 73 } 74 printf("%d ",ans); 75 } 76 } 77 return 0; 78 }