3524: [Poi2014]Couriers
Time Limit: 20 Sec Memory Limit: 256 MB[ Submit][ Status][ Discuss]
Description
给一个长度为n的序列a。1≤a[i]≤n。
m组询问,每次询问一个区间[l,r],是否存在一个数在[l,r]中出现的次数大于(r-l+1)/2。如果存在,输出这个数,否则输出0。
Input
第一行两个数n,m。
第二行n个数,a[i]。
接下来m行,每行两个数l,r,表示询问[l,r]这个区间。
Output
m行,每行对应一个答案。
Sample Input
7 5
1 1 3 2 3 4 3
1 3
1 4
3 7
1 7
6 6
1 1 3 2 3 4 3
1 3
1 4
3 7
1 7
6 6
Sample Output
1
0
3
0
4
0
3
0
4
HINT
【数据范围】
n,m≤500000
2016.7.9重设空间,但未重测!
Source
找一个出现次数最多的数,比较是左儿子大还是右儿子大,再往下找
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N = 500000 + 5;
int n,m,cnt,id,a[N],p[N],num[N],v[N],ans;
int sum[N*20],ls[N*20],rs[N*20],root[N];
bool cmp( int x, int b ){ return a[x] < a[b]; }
void build( int &k, int l, int r, int x ){
ls[++id] = ls[k]; rs[id] = rs[k]; sum[id] = sum[k]+1; k = id;
if( l == r ) return ;
int mid = (l+r)>>1;
if( x <= mid ) build( ls[k], l, mid, x );
else build( rs[k], mid+1, r, x );
}
int query( int x, int y, int l, int r, int k ){
if( l == r ) return l;
int mid = (l+r)>>1;
int sl = sum[ls[y]]-sum[ls[x]], sr = sum[rs[y]]-sum[rs[x]];
if( sl >= k ) return query( ls[x], ls[y], l, mid, k );
else if( sr >= k ) return query( rs[x], rs[y], mid+1, r, k );
else return 0;
}
int main(){
scanf("%d%d", &n, &m);
for( int i = 1; i <= n; i++ ) scanf("%d", &a[i]), p[i]=i;
std::sort(p+1,p+n+1,cmp);
for( int i = 1; i <= n; i++ ) num[p[i]] = (a[p[i]] != a[p[i-1]]) ? ++cnt : cnt;
for( int i = 1; i <= n; i++ ) v[num[i]] = a[i];
for( int i = 1; i <= n; i++ ){
root[i] = root[i-1];
build( root[i], 1, cnt, num[i] );
}for( int i = 1,x,y; i <= m; i++ ){
scanf("%d%d", &x, &y);
ans = query(root[x-1],root[y],1,cnt,(y-x+1)/2+1);
printf("%d\n", v[ans]);
}
return 0;
}