Bzoj3524 [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

 

可持久化线段树。将数列从1到n位置依次加入可持久化线段树中,并将插入顺序作为时间顺序。

询问[L,R]区间中出现的数,可以利用时间R时的状态减去L-1时的状态,得到[L,R]区间内的数的数量,然后二分找出目标数即可。

 

/*by SilverN*/
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int mxn=500010;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m;
struct node{
    int lc,rc;
    int sum;
}t[mxn*20];
int root[mxn];
int nct=0;
void update(int la,int v,int l,int r,int &rt){
    rt=++nct;
    t[rt]=t[la];
    t[rt].sum++;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(v<=mid)update(t[la].lc,v,l,mid,t[rt].lc);
    else update(t[la].rc,v,mid+1,r,t[rt].rc);
    return;
}
int query(int L,int R){
    int l=1,r=n;
    int mid,la=root[L-1],y=root[R],tmp=(R-L+1)/2;
    while(l!=r){
        if(t[y].sum-t[la].sum<=tmp)return 0;
        mid=(l+r)>>1;
        if(t[t[y].lc].sum-t[t[la].lc].sum>tmp){
            r=mid;
            la=t[la].lc;
            y=t[y].lc;
        }
        else if(t[t[y].rc].sum-t[t[la].rc].sum>tmp){
        //else:不可能两边次数都多于(R-L+1)/2 所以可以二分
            l=mid+1;
            la=t[la].rc;
            y=t[y].rc;
        }
        else return 0;
    }
    return l;
}
int main(){
    n=read();m=read();
    int i,j,x,y,l,r;
    for(i=1;i<=n;i++){
        x=read();
        update(root[i-1],x,1,n,root[i]);
    }
    for(i=1;i<=m;i++){
        x=read();y=read();
        printf("%d\n",query(x,y));
    }
    return 0;
}  

 

posted @ 2016-11-27 15:46  SilverNebula  阅读(192)  评论(0编辑  收藏  举报
AmazingCounters.com