[Poi2014]Couriers

Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 2210  Solved: 853
[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

Sample Output

1
0
3
0
4

HINT

【数据范围】

n,m≤500000


2016.7.9重设空间,但未重测!


Source

By Dzy

思路

主席树+权值线段树

两颗树确定区间,然后树上跑二分。

代码实现

 1 #include<cstdio>
 2 const int maxn=5e5+10;
 3 int n,m,s[maxn];
 4 int hd[maxn];
 5 int ps,t[maxn<<5],ls[maxn<<5],rs[maxn<<5];
 6 int a,b,c;
 7 void add(int pre,int suc,int l,int r,int v){
 8     t[suc]=t[pre]+1;
 9     if(l==r) return;
10     ls[suc]=ls[pre],rs[suc]=rs[pre];
11     int mid=l+r>>1;
12     if(v<=mid) ls[suc]=++ps,add(ls[pre],ls[suc],l,mid,v);
13     else rs[suc]=++ps,add(rs[pre],rs[suc],mid+1,r,v);
14 }
15 int search(int pre,int suc,int l,int r,int v){
16     while(l!=r){
17         int mid=l+r>>1;
18         if(t[ls[suc]]-t[ls[pre]]>v) suc=ls[suc],pre=ls[pre],r=mid;
19         else if(t[rs[suc]]-t[rs[pre]]>v) suc=rs[suc],pre=rs[pre],l=mid+1;
20         else return 0;
21     }
22     return l;
23 }
24 int main(){
25     scanf("%d%d",&n,&m);
26     for(int i=1;i<=n;i++){
27         scanf("%d",&a);
28         hd[i]=++ps;
29         add(hd[i-1],hd[i],1,n,a);
30     }
31     for(int i=1;i<=m;i++){
32         scanf("%d%d",&b,&c);
33         printf("%d
",search(hd[b-1],hd[c],1,n,c-b+1>>1));
34     }
35     return 0;
36 }
原文地址:https://www.cnblogs.com/J-william/p/6950241.html