【BZOJ3524/2223】[Poi2014]Couriers 主席树

【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

题解:同BZOJ2223

主席树水题小结:面对这些无修改的主席树,想一想如果询问改成“在1~n的区间内,求...”,然后想出用线段树的解法,再放到主席树上看一看能不能用两棵线段树的差来表示就行了

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
struct node
{
    int ls,rs,siz;
}s[10000000];
int n,m,tot;
int root[500010];
int readin()
{
    int ret=0,f=1;    char gc=getchar();
    while(gc<'0'||gc>'9'){if(gc=='-')f=-f;    gc=getchar();}
    while(gc>='0'&&gc<='9')    ret=ret*10+gc-'0',gc=getchar();
    return ret*f;
}
void pushup(int x)
{
    s[x].siz=s[s[x].ls].siz+s[s[x].rs].siz;
}
void insert(int &x,int &y,int l,int r,int p)
{
    y=++tot;
    if(l==r)
    {
        s[y].siz=s[x].siz+1;
        return ;
    }
    int mid=l+r>>1;
    if(p<=mid)    s[y].rs=s[x].rs,insert(s[x].ls,s[y].ls,l,mid,p);
    else    s[y].ls=s[x].ls,insert(s[x].rs,s[y].rs,mid+1,r,p);
    pushup(y);
}
int query(int x,int y,int l,int r,int p)
{
    if(l==r)    return l;
    int mid=l+r>>1;
    if(s[s[y].ls].siz-s[s[x].ls].siz>p)    return query(s[x].ls,s[y].ls,l,mid,p);
    if(s[s[y].rs].siz-s[s[x].rs].siz>p)    return query(s[x].rs,s[y].rs,mid+1,r,p);
    return 0;
}
int main()
{
    n=readin(),m=readin();
    int i,a,b;
    for(i=1;i<=n;i++)    a=readin(),insert(root[i-1],root[i],1,n,a);
    for(i=1;i<=m;i++)
    {
        a=readin(),b=readin();
        printf("%d\n",query(root[a-1],root[b],1,n,b-a+1>>1));
    }
    return 0;
}
posted @ 2017-01-16 16:27  CQzhangyu  阅读(246)  评论(0编辑  收藏  举报