P3567 [POI2014]KUR-Couriers

本文介绍了解决LuoguP3567[POI2014]KUR-Couriers问题的方法,通过使用主席树进行区间查询,解决了数出现次数的问题,并给出了完整的代码实现。

LuoguP3567 [POI2014]KUR-Couriers

菜死了,竟然没看出来这是一道主席树的模板题.还自己瞎YY了写奇技淫巧。
首先\([l,r]\)这个区间的主席树我们是很好求的。
如果一个数出现的次数严格大于\((r - l + 1) / 2\)
那么,第\((r - l + 1) /2\)大的数肯定是他,
但是,怎么判断第\((r - l + 1)/2\)大的数到底出现没出现更多次,好像没有思路。
但是,在递归查询第\(k\)大的时候,查询的排名不随区间改变而改变就好了
煮个栗子
普通的主席树如果左儿子(默认使用当前区间的主席树)有\(k - 1\)个数
我们将问题转化为查询右儿子第一大
但是这道题去查询右儿子第\(k\)大,如果右儿子也不足\(k\),说明没有数出现了\((r - l + 1) /2\)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cctype>
using namespace std;
const int N = 5e5 + 3;
struct node{
    int sum;
    int lc,rc;  
}a[N * 30];
int v[N],b[N],rt[N];
int n,m,t;
inline int read(){
    int v = 0,c = 1;char ch = getchar();
    while(!isdigit(ch)){
        if(ch == '-') c = -1;
        ch = getchar(); 
    }
    while(isdigit(ch)){
        v = v * 10 + ch - 48;
        ch = getchar(); 
    }
    return v * c;   
}
inline void ins(int &u,int l,int r,int x){
    a[++t] = a[u];
    u = t;
    if(l == r){
        a[u].sum ++;
        return ;
    }
    int mid = (l + r) >> 1;
    if(x <= mid) ins(a[u].lc,l,mid,x);
    else ins(a[u].rc,mid + 1,r,x);
    a[u].sum = a[a[u].lc].sum + a[a[u].rc].sum;
}
inline int query(int u1,int u2,int l,int r,int x){
    if(l == r) return l;
    int s1 = a[a[u2].lc].sum - a[a[u1].lc].sum,s2 = a[a[u2].rc].sum - a[a[u1].rc].sum;
//  printf("%d %d\n",s1,s2);
    int mid = (l + r) >> 1;
    if(s1 > x) return query(a[u1].lc,a[u2].lc,l,mid,x);
    else if(s2 > x) return query(a[u1].rc,a[u2].rc,mid + 1,r,x);
    else return 0;  
}
int main(){
    n = read(),m = read();
    for(int i = 1;i <= n;++i) v[i] = b[i] = read();
    sort(b + 1,b + n + 1);
    b[0] = unique(b + 1,b + n + 1) - b - 1;
    for(int i = 1;i <= n;++i){
        rt[i] = rt[i - 1];
        v[i] = lower_bound(b + 1,b + b[0] + 1,v[i]) - b;
        ins(rt[i],1,b[0],v[i]);
    }
    while(m--){
        int l = read(),r = read();
        int k = (r - l + 1) / 2;
        int ans = query(rt[l - 1],rt[r],1,b[0],k);
        printf("%d\n",ans == 0 ? ans : b[ans]); 
    }
    return 0;   
}

转载于:https://www.cnblogs.com/wyxdrqc/p/10641271.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值