luogu P4137 mex

题面:

有一个长度为$n$的数组${a1,a2,…,an}$。$m$次询问,每次询问一个区间内最小没有出现过的自然数。

 

令$lst[i][r]$表示在$[1, r]$中数值$i$最后出现的位置

那么,我们要求的便是$min(t)$

使得$lst[t][r] \geqslant l(0 \leqslant t \leqslant t - 1)$

 

注意到$lst[][i]$相比于$lst[][i - 1]$其实只有一个地方变化了

可以考虑用可持久化线段树

$t$可以选择在线段树上二分,相应地需要维护$lst[]$的最小值

都挺简单,相信你们都会打代码吧

 

果然有不是差分的题。。。。。

 

#include <cstdio>
#include <algorithm>
#include <iostream>
#define sid 10000050
#define ri register int
using namespace std;

char RR[23345];
extern inline char gc() {
    static char *S = RR + 22000, *T = RR + 22000;
    if(S == T) fread(RR, 1, 22000, stdin), S = RR;
    return *S ++;
}
inline int read() {
    int p = 0, w = 1; char c = gc();
    while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
    while(c >= '0' && c <= '9') { p = p * 10 + c - '0'; c = gc(); }
    return p * w;
}

int n, m, cnt, tot;
int rt[200050], ls[sid], rs[sid], minhp[sid];

void Insert(int &now, int pre, int l, int r, int val, int pos) {
    now = ++ cnt;
    ls[now] = ls[pre]; rs[now] = rs[pre];
    if(l == r) { minhp[now] = pos; return; }
    int mid = (l + r) >> 1;
    if(val <= mid) Insert(ls[now], ls[pre], l, mid, val, pos);
    else Insert(rs[now], rs[pre], mid + 1, r, val, pos);
    minhp[now] = min(minhp[ls[now]], minhp[rs[now]]); 
}

int Query(int R, int l, int r, int L) {
    if(l == r) return l;
    int mid = (l + r) >> 1;
    if(minhp[ls[R]] < L) return Query(ls[R], l, mid, L);
    else return Query(rs[R], mid + 1, r, L);
}

int main() {
    n = read(); m = read();
    for(ri i = 1; i <= n; i ++) {
        int u = read();
        if(u >= n) rt[i] = rt[i - 1];
        else Insert(rt[i], rt[i - 1], 0, n, u, i);
    }
    for(ri i = 1; i <= m; i ++) {
        int l, r;
        l = read(); r = read();
        printf("%d\n", Query(rt[r], 0, n, l));
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/reverymoon/p/9308204.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值