Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 13911 | Accepted: 5098 |
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
Source
给出一个单调非减序列,询问指定 区间出现最频繁的值的出现次数。
利用线段树做,每个节点保存出现的最大频数cnt,假如查询区间[a,b],对于区间为[l,r]的节点,如果[a,b]在其[l,r]左半部分(b<=m),则递归到左子树求解,若在又右半部分(a>m)则递归到右子树。
假如a<=m<b,则查询左子树[a,m]的最大值和右子树[m+1,b]的最大值 cnt = max(lchild.cnt, rchild.cnt)。由于是单调非减的,所以出现次数最多的数可能一部分在左半区间的末端,另一部分在右半区间的起始端,所以要判断if(data[m]==data[m+1]) /*数据都存在data数组中*/,不等于(也就是小于)直接返回cnt。如果等于二分查找data[m]在左半区间出现的次数,和右半区间的次数。更新cnt最大值
这个方法交c++ 1079ms。。。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 100005
struct node
{
int l, r;
int cnt;
node() {}
node(int ll, int rr){ l = ll; r = rr;}
};
int data[maxn];
node tr[4*maxn];
void build(int rt, int l, int r)
{
tr[rt] = node(l, r);
if(l == r){
tr[rt].cnt = 1;
return;
}
int m = l+(r-l)/2;
int lc = 2*rt, rc = 2*rt+1;
build(lc, l, m);
build(rc, m+1, r);
tr[rt].cnt = max(tr[lc].cnt, tr[rc].cnt);
if(data[m] == data[m+1]){
int v = data[m];
int cnt = m -(lower_bound(data+l, data+m+1, v)-data)+1;
cnt += (upper_bound(data+m+1, data+r+1, v)-data)-(m+1);
tr[rt].cnt = max(tr[rt].cnt, cnt);
}
}
int query(int rt, int ll, int rr)
{
int l = tr[rt].l, r = tr[rt].r;
if(l == ll && r == rr)
return tr[rt].cnt;
int m = l+(r-l)/2;
int lc = 2*rt, rc = 2*rt+1;
if(rr <= m)
return query(lc, ll, rr);
if(ll > m)
return query(rc, ll, rr);
int ret = max(query(lc,ll, m), query(rc, m+1, rr));
if(data[m] == data[m+1]){
int v = data[m];
int cnt = m -(lower_bound(data+ll, data+m+1, v)-data)+1;
cnt += (upper_bound(data+m+1, data+rr+1, v)-data)-(m+1);
ret = max(cnt, ret);
}
return ret;
}
int main()
{
int n, q;
while(~scanf("%d", &n) && n){
scanf("%d", &q);
for(int i = 1; i <= n; i++)
scanf("%d", data+i);
build(1, 1, n);
int a,b;
for(int i = 0; i < q; i++){
scanf("%d%d", &a, &b);
printf("%d\n", query(1, a, b));
}
}
return 0;
}
还有一种做法是将数据进行离散化处理,将相同的数合并,并记录每个数出现的次数,再建立线段树记录出现的最大次数。每次询问[a,b],利用前缀和cnt[n]预处理计算前n个数总共出现多次,每次先二分查找a和b对应的下标位置l和r,对于完全包含的a,b里面的数[l+1,r-1]用线段树查询最大值,再跟cnt[l]-a+1, b-cnt[r-1]进行比较得到最大值
用c++跑625ms....
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 100005
struct node
{
int l, r;
node(){}
node(int ll , int rr) {l = ll; r = rr;}
};
node tree[4*maxn];
int maxi[4*maxn];
int data[maxn];
int cnt[maxn];
void build(int rt, int l, int r)
{
tree[rt] = node(l, r);
if(l == r){
maxi[rt] = cnt[l]-cnt[l-1];
return;
}
int m = l+(r-l)/2;
int lc = 2*rt, rc = 2*rt+1;
build(lc, l, m);
build(rc, m+1, r);
maxi[rt] = max(maxi[lc], maxi[rc]);
}
int query(int rt, int ll, int rr)
{
if(ll > rr) return 0;
int l = tree[rt].l, r = tree[rt].r;
if(l == ll && r == rr)
return maxi[rt];
int m = l+(r-l)/2;
if(rr <= m)
return query(2*rt, ll, rr);
else if(ll > m)
return query(2*rt+1, ll, rr);
else
return max(query(2*rt, ll, m), query(2*rt+1, m+1, rr));
}
int main()
{
int n, q;
while(~scanf("%d",&n) && n){
scanf("%d", &q);
for(int i = 1; i <= n; i++)
scanf("%d", data+i);
data[0] = data[1];
int cur = 1;
int tot = 0;
cnt[0] = 0;
for(int i = 1; i <= n; i++){
if(data[i] == data[i-1])
tot++;
else{
data[cur] = data[i-1];
cnt[cur] = cnt[cur-1]+tot;
tot = 1;
cur++;
}
}
cnt[cur] = cnt[cur-1]+tot;
build(1, 1, cur);
int a, b;
for(int i = 0; i < q; i++){
scanf("%d%d", &a, &b);
int l = lower_bound(cnt+1, cnt+cur+1, a)-cnt;
int r = lower_bound(cnt+1, cnt+cur+1, b)-cnt;
if(l == r)
printf("%d\n",b-a+1);
else{
int ans;
ans = query(1, l+1, r-1);
ans = max(cnt[l]-a+1, ans);
ans = max(ans, b-cnt[r-1]);
printf("%d\n", ans);
}
}
}
return 0;
}