[Poi2014]Couriers
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
Sample Output
1
0
3
0
4
HINT
【数据范围】
n,m≤500000
Source
By Dzy
Solution :
可持久化线段树
对于每一个位置i,维护一棵权值线段树,保留1~i位置上所有数的信息。
Code :
#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
template<typename T> inline void read(T &x){
x = 0; T f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
const int N = 500000 + 10;
int
root[N], lc[N * 20], rc[N * 20], sum[N * 20];
int
n, m, a[N], SZ;
inline void modify(int l, int r, int last, int &now, int pos){
now = ++SZ;
sum[now] = sum[last] + 1;
if (l == r) return;
int mid = l+r >> 1;
if (pos <= mid){
rc[now] = rc[last];
modify(l, mid, lc[last], lc[now], pos);
}else{
lc[now] = lc[last];
modify(mid+1, r, rc[last], rc[now], pos);
}
}
inline void query(int l, int r, int L, int R, int p){
if (l == r){
printf("%d\n", sum[R] - sum[L] > p ? l : 0); return;
}
int mid = l+r >> 1;
if (sum[lc[R]] - sum[lc[L]] > sum[rc[R]] - sum[rc[L]]) query(l, mid, lc[L], lc[R], p);
else query(mid+1, r, rc[L], rc[R], p);
}
int main(){
read(n); read(m);
rep(i, 1, n) read(a[i]);
rep(i, 1, n) modify(1, n, root[i-1], root[i], a[i]);
rep(i, 1, m){
int l, r; read(l); read(r); if (l > r) swap(l, r);
query(1, n, root[l-1], root[r], (r-l+1)/2);
}
return 0;
}