BZOJ 3524 [Poi2014]Couriers(可持久化线段树)

 

【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=3524

 

【题目大意】

  给一个长度为n的序列a。1≤a[i]≤n。
  m组询问,每次询问一个区间[l,r],是否存在一个数在[l,r]中出现的次数大于(r-l+1)/2。
  如果存在,输出这个数,否则输出0。

 

【题解】

  建立可持久化的权值线段树,对于区间查询,
  在线段树上二分查询R和L-1版本间的数值差符合要求的位置。

 

【代码】

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=500010;
int n,m,i,x,y,z,ans;
int l[N*40],r[N*40],v[N*40],tot,root[N],a[N],pre[N];
void read(int&a){
    char ch;while(!((ch=getchar())>='0')&&(ch<='9'));
    a=ch-'0';while(((ch=getchar())>='0')&&(ch<='9'))a*=10,a+=ch-'0';
}
int build(int a,int b){
    int x=++tot; v[x]=0;
    if(a==b)return x;
    int mid=(a+b)>>1;
    return l[x]=build(a,mid),r[x]=build(mid+1,b),x;
}
// x版本c位置+p,返回更新后版本根节点id 
int change(int x,int a,int b,int c,int p){
    int y=++tot;v[y]=v[x]+p;
    if(a==b)return y;
    int mid=(a+b)>>1;
    if(c<=mid)l[y]=change(l[x],a,mid,c,p),r[y]=r[x];
    else l[y]=l[x],r[y]=change(r[x],mid+1,b,c,p);
    return y;
}
// 查询[lx,rx]区间内超过cnt的数(cnt>=区间长1/2) 
int query(int lx,int rx,int cnt){
    int L=1,R=n,mid,x,y;
    x=root[lx-1],y=root[rx];
    while(L!=R){
        if(v[y]-v[x]<=cnt)return 0;
        mid=(L+R)>>1;
        if(v[l[y]]-v[l[x]]>cnt)R=mid,x=l[x],y=l[y];
        else if(v[r[y]]-v[r[x]]>cnt)L=mid+1,x=r[x],y=r[y];
        else return 0;
    }return L;
}
int main(){
    while(~scanf("%d%d",&n,&m)){
        root[tot=0]=build(1,n);
        for(int i=1;i<=n;i++){
            int x; read(x);
            root[i]=change(root[i-1],1,n,x,1);
        }
        for(int i=1;i<=m;i++){
            int l,r;
            scanf("%d%d",&l,&r);
            printf("%d\n",query(l,r,(r-l+1)>>1));
        }
    }return 0;
}
posted @ 2017-04-15 23:02  forever97  阅读(191)  评论(0编辑  收藏  举报