[POI 2014]Couriers(主席树+二分)

题目链接

http://main.edu.pl/en/archive/oi/21/kur

题目大意

给定一个序列,对于每个询问 [Li,Ri] ,要在这个询问区间里找出一个数字,并且这个数字在这个区间中的出现次数要大于 [(RiLi+1)2] ,若无这个数字,则输出-1

思路

首先对于这个序列,建立一个静态的主席树,然后对于每次询问的区间,用前缀 Ri 和前缀 Li 的主席树做差,在得到的主席树里从根节点开始向下走,二分区间,即可找到这个数字

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXN 500010

using namespace std;

int n,m;
int root[MAXN],lc[MAXN*20],rc[MAXN*20],sum[MAXN*20];
int nCount=0;

void update(int last,int &o,int L,int R,int pos)
{
    o=++nCount;
    sum[o]=sum[last]+1;
    if(L==R) return;
    int M=(L+R)>>1;
    lc[o]=lc[last],rc[o]=rc[last]; //!!!!!
    if(pos<=M) update(lc[last],lc[o],L,M,pos);
    else update(rc[last],rc[o],M+1,R,pos);
}

int query(int last,int o,int L,int R,int K)
{
    if(sum[o]-sum[last]<=K) return 0;
    if(L==R) return L;
    int M=(L+R)>>1;
    if(sum[lc[o]]-sum[lc[last]]>K)
        return query(lc[last],lc[o],L,M,K);
    else if(sum[rc[o]]-sum[rc[last]]>K)
        return query(rc[last],rc[o],M+1,R,K);
    else return 0;
}
/* int query(int L,int R) { int l=1,r=n,mid,x,y,tmp=(R-L+1)>>1; x=root[L-1];y=root[R]; while(l!=r) { if(sum[y]-sum[x]<=tmp)return 0; mid=(l+r)>>1; if(sum[lc[y]]-sum[lc[x]]>tmp) {r=mid;x=lc[x];y=lc[y];} else if(sum[rc[y]]-sum[rc[x]]>tmp) {l=mid+1;x=rc[x];y=rc[y];} else return 0; } return l; }*/

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        update(root[i-1],root[i],1,n,x);
    }
    for(int i=1;i<=m;i++)
    {
        int L,R;
        scanf("%d%d",&L,&R);
        printf("%d\n",query(root[L-1],root[R],1,n,(R-L+1)>>1));

        //printf("%d\n",query(L,R));
    }
    return 0;
}

你可能感兴趣的:([POI 2014]Couriers(主席树+二分))