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

21 篇文章 0 订阅
8 篇文章 0 订阅

题目链接

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值