Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 15763 | Accepted: 5732 |
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
题意 : 给一个由n个元素组成的非严格上升的数组, q个询问(L,R), 输出位置在L~R的元素重复最多的个数
思路 : 由于是上升序列,所以重复的元素一定是在一起的,显然可以将原数组num[n]转化g[size],g[t]表示第t个重复元素出现的个数,size表示不同元素的个数。(O(N))
对于每个询问(L,R),首先求出num[L]和num[R]分别属于第几个重复的元素(即对应的g[]下标),方便缩小答案的范围。定义sum[]数组为g[]的前缀和,利用二分查找即可。
找出L,R对应的重复元素的在g[]的次序分别为l,r,考虑到首尾元素可能小于首尾元素重复的个数,就是L,R可能在重复元素的中间,则单独考虑。剩下的就是g[l+1]~g[r-1](注意l==r的情况答案就是R-L+1),也就是问题转化为求g[]在[l+1,r-1]的最大值以及首尾重复的数字个数k1,k2,他们之间的最大值即是答案。静态求数组区间最大值,线段树O(log size)或者平方分割O(sqrt(size))均可;
例如num[] = {-1,-1,1,1,1,1,3,10,10,10},n = 10;可转化为g[]={2,4,1,3},size = 4;sum[]={2,6,7,10};
对于询问(L=2,R=9),num[L]对应g[1],num[R]对应g[4],所以,l=1,r=4;k1 = 1,k2 = 2;而g[]在[2,3]最大为4;
故答案为4,k1,k2中最大值,即ans = 4;
附AC代码之前,向大神们求问此题另一解法是否可行,开始看错题目,没有看到是上升序列,就是{1,2,1,5,3,1,2,3}也是合法的,所以想到了离散化+线段树+莫队算法,先将数组离散化成[1,n]整数,对q个询问区间进行莫队算法,用线段树维护每个数出现的次数,并可logn返回数值区间出现的最多的次数。总觉得这个方法也是可行的,然而不会曼哈顿最小生成树,然后不知道写搓了还是卡了常数,悲伤的TLE了。
poj3368 AC代码,用的平方分割,没有线段树快。
Run ID | User | Problem | Result | Memory | Time | Language | Code Length | Submit Time |
15184614 | shnu_boy | 3368 | Accepted | 1084K | 1266MS | G++ | 2063B | 2016-02-21 17:29:37 |
//#include<bits/stdc++.h>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <ctime>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <utility>
using namespace std;
int const maxnum = 0x3f3f3f3f;
int const maxn = 1e5 + 10;
int num[maxn],sum[maxn];
int n,q;
int block,b;//每个block数字个数容量
int s[maxn];//每个block的最大值
void init()
{
//memset(s,0,sizeof(s));
block = (int)ceil(sqrt((double)b));
for(int i = 1 ; i*block <= b ; i ++ )
{
s[i] = -maxnum;
for(int j = (i-1)*block+1 ; j <= block*i ; j ++ )
{
s[i] = s[i] > num[j] ? s[i] : num[j];
}
}
}
int find(int pos)
{
//pos对应的num序数
int high = b,low = 1,mid;
while(low < high)
{
mid = ((high+low)>>1);
if(sum[mid] < pos)low = mid+1;
else if(sum[mid] == pos)high = low = mid;
else high = mid;
}
return low;
}
int Ans(int L,int R)
{
int l=find(L),r=find(R);
if(l == r){
return R-L+1;
}
int f1 = sum[l]-L+1,f2 = R-sum[r-1];
l++;r--;
int ans = f1 > f2 ? f1 : f2;
if(r-l <= block)
{
for(int t = l ; t <= r ; t ++ )
{
if(num[t] > ans)
ans = num[t];
}
}
else
{
f1 = f2 = 0;
for(int t = l ; t <= (l-1)/block*block+block ; t ++ )
{
if(num[t] > f1)
f1 = num[t];
}
for(int t = r ; t >= (r-1)/block*block+1 ; t -- )
{
if(num[t] > f2)
f2 = num[t];
}
if(f2 > f1)f1 = f2;
if(f1 > ans)ans = f1;
for(int t = (l-1)/block+2; t <= (r-1)/block ; t ++ )
{
if(ans < s[t])
ans = s[t];
}
}
return ans;
}
int main()
{
while(cin >> n && n)
{
cin >> q;
b = 0;//block的数量
int k = maxnum;//扫描数字的前一个数字
for(int t = 1 ; t <= n ; t ++ )
{
int g;//扫描的数字
scanf("%d",&g);
if( g - k )
{
b ++;
num[b] = 1;
k = g;
}
else
{
num[b] ++;
}
}
for(int t = 1 ; t <= b ; t ++ )
{
sum[t] = sum[t-1] + num[t];
}
init();
for(int t = 1 ; t <= q ; t ++ )
{
int L,R;
scanf("%d%d",&L,&R);
printf("%d\n",Ans(L,R));
}
}
return 0;
}