HDU-1806
Problem 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
分析:
首先我们注意到数组是非降序的,所以其肯定是有序的,且所有相等的元素会聚集在一起
我们就可以对这个数组进行一次编码
例如:
1 2 2 3 3 4 5 5 6
可以编码为
(1,1),(2,2),(3,2),(4,1),(5,2),(6,1)
(x,y)表示有x个连续的b
也就是将这个数组进行了分段,接下来利用两个数组
value[i]表示第i段的数字是多少
count[i]表示第i段的数字有多少个
再来一个数组将原数组中每个数字的所在的段进行记录
num[i]表示第i个数字所在段。
left[i]表示第i个数字所在的段左端点
right[i]表示第i个数字所在段的右端点
然后对于每一次询问,(L,R)
1.如果l,r是在一个段中,直接输出r-l+1
2.如果l,r不在一个段中,中间只间隔一个段
max(right[l]-l,r-left[r],count(nums[l]+1))
3.如果l,r不在一个段中,且中间间隔一大于段,由以下公式得到答案
max(right[l]-l,r-left[r],rmq[nums[l]+1,nums[r]-1])
其中的rmq就是范围最大值问题,就可以利用线段树进行解决了,表示区间内的出现次数最多的值出现的次数
代码实现:实现比较粗糙,只是展示思路。
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
while(true){
int n = input.nextInt();
if(n == 0){
return;
}
int q = input.nextInt();
int[] nums = new int[n];
for(int i = 0;i<n;i++){
nums[i] = input.nextInt();
}
int[][] query = new int[q][2];
for(int i=0;i<q;i++){
query[i][0] = input.nextInt();
query[i][1] = input.nextInt();
}
List<List<Integer>> segment = segment(nums);
// 由于不需要修改,还是不用线段树那么麻烦的方式了,使用ST吧。空间也就懒得算了。
int[][] d = new int[segment.get(0).size()][segment.get(0).size()];
// 注意,这里是用每个段的数量来构造d,而不是原数组。
init(d, segment.get(1));
List<Integer> num = segment.get(4);
for(int i=0;i<q;i++){
int l = query[i][0]-1;
int r = query[i][1]-1;
if(num.get(l) == num.get(r)){
System.out.println(r-l+1);
}else if(num.get(l)+1 == num.get(r)){
System.out.println(Math.max(r-segment.get(2).get(r)+1, segment.get(3).get(l)-l+1));
}else if(num.get(r) - num.get(l)>1){
int max = Math.max(r-segment.get(2).get(r)+1, segment.get(3).get(l)-l+1);
max = Math.max(query(segment.get(4).get(l), segment.get(4).get(r)-2, d), max);
System.out.println(max);
}
}
}
}
public static void init(int[][] d,List<Integer> list){
for(int i=0;i<list.size();i++){
d[i][0] = list.get(i);
}
for(int i=1;(1<<i)<=list.size();i++){
for(int j=0;j+(1<<(i-1))<list.size();j++){
d[j][i] = Math.max(d[j][i-1],d[j+(1<<(i-1))][i-1]);
}
}
}
public static int query(int l,int r,int[][] d){
int k = 0;
while(1<<(k+1)<=r-l+1){
k++;
}
return Math.max(d[l][k], d[r-(1<<k)+1][k]);
}
/**
* 获取出上面所列举的所有信息
* @param nums
* @return
*/
public static List<List<Integer>> segment(int[] nums){
if(nums == null || nums.length == 0){
return null;
}
List<List<Integer>> res = new ArrayList<>();
List<Integer> value = new ArrayList<>();
List<Integer> count = new ArrayList<>();
List<Integer> left = new ArrayList<>();
List<Integer> right = new ArrayList<>();
List<Integer> numss = new ArrayList<>();
res.add(value);
res.add(count);
res.add(left);
res.add(right);
res.add(numss);
int pre = nums[0];
value.add(pre);
left.add(0);
numss.add(1);
int k = 1;
int seg = 1;
int l = 0;
for(int i = 1;i<nums.length;i++){
if(pre!=nums[i]){
value.add(nums[i]);
pre = nums[i];
count.add(k);
k = 1;
left.add(i);
l = i;
int p = left.size()-1;
while(right.size()<p){
right.add(i-1);
}
numss.add(++seg);
}else{
numss.add(seg);
k++;
left.add(l);
}
}
right.add(nums.length-1);
count.add(k);
return res;
}