ZOJ3633 Alice's present 线段树离线 || 主席树在线

题目大意

给一个长度为N的序列,有M个询问,每次询问区间内的数是否全都不一样,如果全都不一样就输出OK,如果有重复的就输出从右到左第一个重复的数。

解题思路

主席树在线非常直观,每一个数对应一个历史版本,然后询问的时候询问那个区间对应的线段树里面有多少个元素,如果最大值是1就表示OK,否则的话就输出最右的非1,对于位置做做处理就行。
线段树离线就不太好办了,由于我前几天刚做完杭州H,那就简单了,首先可以找到对应每一个数,它的左边离它最近的与它相同的数的位置,把它放入线段树这个数的位置,那么我们按照数值为主,位置为副的排序之后,然后就可以O(n)解决这个问题,然后我们对于每一个询问,我们询问区间最大值,如果最大值比左端点小,说明没有重复的,否则的话我们就直接找到最大值的位置的数就是从右往左数第一个重复的数。

AC代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
const int maxn = (int)5e5 + 10;
struct data{
    int val, pos;
    friend bool operator < (const data &a, const data &b){
        if (a.val != b.val) return a.val < b.val;
        else return a.pos < b.pos;
    }
}x[maxn];
int mx[maxn << 2], X[maxn], n, m;
void PushUp(int rt){
    mx[rt] = max(mx[rt << 1], mx[rt << 1 | 1]);
}
void build(int l, int r, int rt){
    if (l == r){
        mx[rt] = 0; return;
    }
    int m = (l + r) >> 1;
    build(lson); build(rson);
    PushUp(rt);
}
void update(int pos, int c, int l, int r, int rt){
    if (l == r){
        mx[rt] = c; return;
    }
    int m = (l + r) >> 1;
    if (pos <= m) update(pos, c, lson);
    else update(pos, c, rson);
    PushUp(rt);
}
int query(int ll, int rr, int l, int r, int rt){
    if (ll == l && rr == r) return mx[rt];
    int m = (l + r) >> 1;
    if (rr <= m) return query(ll, rr, lson);
    else if (ll > m) return query(ll, rr, rson);
    else return max(query(ll, m, lson), query(m + 1, rr, rson));
}
int main(){
    while(~scanf("%d", &n)){
        for (int i = 1; i <= n; i++){
            scanf("%d", &X[i]);
            x[i].val = X[i]; x[i].pos = i;
        }
        sort(x + 1, x + 1 + n);
        build(1, n, 1);
        for (int i = 1; i <= n; i++)
            if (x[i].val == x[i - 1].val) update(x[i].pos, x[i - 1].pos, 1, n, 1);
        scanf("%d", &m);
        for (int i = 1; i <= m; i++){
            int a, b;
            scanf("%d%d", &a, &b);
            int ans = query(a, b, 1, n, 1);
            if (ans < a) puts("OK");
            else printf("%d\n", X[ans]);
        }
        puts("");
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值