bzoj3524 区间问题

来源:互联网 发布:圆筒的体积怎么算c语言 编辑:程序博客网 时间:2024/06/05 02:09

3524: [Poi2014]Couriers

Time Limit: 20 Sec  Memory Limit: 128 MB
Submit: 710  Solved: 215
[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


首先这是个静态区间问题,由于区间是[L,R],我们知道如果L恒为1的话,那么这题可以用线段树或者树状数组轻松解决,这里当然不能按值建树,而应该是离散化后,按下标建树。

那么L不为1的情况,我们考虑是否可以利用前缀和的思想呢?[L,R]可以表示成[1,R]-[1,L-1],显然是可以的,因为线段树记录的下标为i的数出现的次数,这个是满足加减性质。

显然我们不能只维护一颗线段树了,我们要建n棵线段树,对于前i个数(1<=i<=n)建线段树。这样空间会达到O(n^2),我们想是否可以共用一些节点呢?于是就出现了函数式线段树(或者说主席树吧)。

他的原理是对于区间[1,i]的线段树和区间[1,i+1]的线段树,只有logn个节点不同,于是我们可以在[1,i]线段树的基础上插入a[i],沿a[i]插入的路径建立logn个节点,然后维护好左右儿子关系,沿左儿子下去的,右儿子是共用的,同理沿右儿子下去的,左儿子是共用的。具体实现请参考其他资料学习!


代码:

#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>#define L l,mid,ls[x],ls[y]#define R mid+1,r,rs[x],rs[y]#define Maxn 10000010using namespace std;int ls[Maxn],rs[Maxn],s[Maxn];int a[500010],sa[500010],root[500010];int sz,tot;void update(int l,int r,int x,int &y,int v){    y=++sz;    s[y]=s[x]+1;    if(l==r) return;    ls[y]=ls[x],rs[y]=rs[x];    int mid=l+r>>1;    if(v<=mid) update(L,v);    else update(R,v);}int query(int l,int r,int x,int &y,int k){    if(l==r) return l;    int mid=l+r>>1;    if(s[ls[y]]-s[ls[x]]>=k) return query(L,k);    else if(s[rs[y]]-s[rs[x]]>=k) return query(R,k);    return 0;}int main(){    int n,m,l,r;    scanf("%d%d",&n,&m);    for(int i=1;i<=n;i++){        scanf("%d",a+i);        sa[i]=a[i];    }    sort(sa+1,sa+n+1);    tot=unique(sa+1,sa+1+n)-sa-1;    for(int i=1;i<=n;i++){        int t=lower_bound(sa+1,sa+tot+1,a[i])-sa;        update(1,tot,root[i-1],root[i],t);    }    while(m--){        scanf("%d%d",&l,&r);        int ans=query(1,tot,root[l-1],root[r],(r-l+1)/2+1);        if(!ans) puts("0");        else printf("%d\n",sa[ans]);    }return 0;}


0 0