UVa 11235 Frequent values
Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 11241 |
Accepted: 4110 |
Description
You are given a sequence of n integers a1 , a2 , ... , an in non-decreasing order. In addition to that, you are given several queries consisting of indices i and j (1 ≤ i ≤ j ≤ n). For each query, determine the most frequent value among the integers ai , ... , aj.
Input
The input consists of several test cases. Each test case starts with a line containing two integers n and q (1 ≤ n, q ≤ 100000). The next line contains n integers a1 , ... , an (-100000 ≤ ai ≤ 100000, for each i ∈ {1, ..., n}) separated by spaces. You can assume that for each i ∈ {1, ..., n-1}: ai ≤ ai+1. The following q lines contain one query each, consisting of two integers i and j (1 ≤ i ≤ j ≤ n), which indicate the boundary indices for the
query.
The last test case is followed by a line containing a single 0.
Output
For each query, print one line with one integer: The number of occurrences of the most frequent value within the given range.
Sample Input
10 3 -1 -1 1 1 1 1 3 10 10 10 2 3 1 10 5 10 0
Sample Output
1 4 3 -----------------------------------------------------------------------------------------------------------------------------------------------------------
题目:
对于询问(L,R)输出该范围内的最多的重复次数。
思路:
一开始想到的是将每个数的出现次数加入RMQ.A 这样就可以满足题意。
但考虑到一个数可以重复出现多次时间方面不是很乐观,还有更优的方法: 将每段相同数作为一结点,该数字出现次数作为结点信息加入RMQ.A。问题就是如何将pos(L,R)转化到段上(pos是位置),为每一个pos添加信息:
- num[u]: 表示数字u属于的结点标号
- left[u]和right[u]:分别表示该数字段左端点与右端点。
那么对于询问(L,R)可以如是组织答案:
ans=max{ right[L]-L+1 , R-left[R]+1 , RMQ(num[L]+1,num[R]-1)}
需要注意的是一些边界的处理,如果处理不好可能会死程序。
*在长度与序号的运算中:
序号+长度-1=序号
序号-长度+1=序号
序号-序号+1=长度
代码:
1 #include<cstdio> 2 #include<vector> 3 #define FOR(a,b,c) for(int a=(b);a<(c);a++) 4 using namespace std; 5 6 const int maxn = 100000+10; 7 const int maxlog = 20; 8 9 struct RMQ{ //范围最大值 10 int d[maxn][maxlog]; 11 12 //DP 13 void init(const vector<int>& A){ 14 int n=A.size(); 15 FOR(i,0,n) d[i][0]=A[i]; 16 for(int j=1;(1<<j)<=n;j++) 17 for(int i=0;i+(1<<j)-1<n;i++) //i+(1<<j)-1 :序号 < n 18 d[i][j]=max(d[i][j-1],d[i+(1<<(j-1))][j-1]); //1<<(j-1) 19 } 20 21 int Query(int L,int R){ //建立在L<R的基础上 22 int k=0; 23 while(1<<(k+1) <= (R-L+1)) k++; 24 return max(d[L][k],d[R-(1<<k)+1][k]); 25 } 26 }; 27 28 int a[maxn],num[maxn],left[maxn],right[maxn]; 29 int n,m; 30 RMQ rmq; 31 32 int main(){ 33 while(scanf("%d%d",&n,&m)==2 && n){ 34 FOR(i,0,n) scanf("%d",&a[i]); 35 a[n]=a[n-1]+1; //哨兵 //保证最后一段的处理 ** 36 vector<int> count; 37 //RMQ.A中每一段相同数字为一结点,数值为长度,所以RMQ 提供操作Query为 L段到R段的最大长度 38 int start=-1; 39 FOR(i,0,n+1) if(i==0 || a[i-1]<a[i]){ //新的一段 40 //处理旧的一段 41 if(i>0){ //if i==0 则只有start重载为0 42 count.push_back(i-start); //将旧一段的长度加入RMQ.A 43 FOR(j,start,i){ //赋值 旧一段每个位置p 信息num left right 44 num[j]=count.size()-1; left[j]=start; right[j]=i-1; 45 //num记录结点标号 46 } 47 } 48 start = i; //开始新的一段 start记录开始一段的头序号 49 } 50 rmq.init(count); 51 FOR(i,0,m){ 52 int L,R; 53 scanf("%d%d",&L,&R); L--;R--; //缩 序号 54 int ans=0; 55 if(num[L]==num[R]) ans=R-L+1; //特殊情况 LR处于一段 56 else { 57 ans=max(right[L]-L+1,R-left[R]+1); 58 if(num[L]+1 < num[R]) ans=max(ans,rmq.Query(num[L]+1,num[R]-1)); 59 //if num[L]+1==num[R]则RMQ 调用出错 60 } 61 printf("%d\n",ans); 62 } 63 } 64 return 0; 65 }