HDU-6703 array (线段树)

题意

一个长度为n的排列a,\(\forall i\in [1,n] ,1\le a_i \le n\) , m次操作,每次操作:

  1. (1,pos),把 \(a_{pos}\) 变为\(a_{pos} + 10000000\)
  2. (2,r,k) ,找到最小的一个值x,使得\(\forall i\in [1,r], x \neq a_i, x\ge k\)

数据范围:

\(1\le n\le 100000,1\le m\le 100000,1\le r\le n,1\le k\le n\)

分析

  1. 观察每个数的值域,都在\([1,n]\) 之间,而k的范围也在\([1,n]\) 之间,所以答案最终只会在\([1,n+1]\) 之间。

  2. 操作1会使一个数字\(a_i\)加1e7,而这个数已经远远超过n和k,所以也就代表着\(a_i\)从原序列中被删除了。

  3. 由第一条可以知道,每次查询的答案只会在[k,n+1]上,而序列中的数只会在[1,n](进行过1操作的直接删除,不再考虑)。所以我们可以每次查询序列中有没有出现在\([k,n]\) 的数,并且他们的下标是大于r的。如果之前删除过一个数\(x\),那么就把这个下标变成大于n就可以了, 这样对于\(k\le x\) 的情况,\(x\) 所对应的下标都是大于\(r\) 的(也就是把x作为候选答案)

如何维护查询所需要的东西?权值线段树维护区间权值最大下标

  1. 对于操作1,直接让单点的下标变为n+1
  2. 对于操作2,找被区间[k,n]的包含的结点,对于这些结点,如果左节点的最大下标大于r,则递归左节点,否则看右结点,如果都没有,就返回n+1(代表答案候选为n+1)。而递归到终点时,看单点维护的下标是否大于r,如果大于,就返回单点的权值,否则返回n+1.
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
const int inf = 0x3f3f3f3f;
int a[N],b[N];
struct SegTree{
    int l,r,id;
}t[4*N];
int n,m;
void build(int p,int l,int r){
    t[p].l = l;t[p].r = r;
    if(l == r){
        t[p].id = b[l];
        return;
    }
    int mid = l + r >> 1;
    build(p*2,l,mid);
    build(p*2+1,mid+1,r);
    t[p].id = max(t[p*2].id,t[p*2+1].id);
}
void change(int p,int x){
    if(t[p].l == t[p].r && t[p].l == x){
        t[p].id = n+1;return;//删除该数,将维护的下标变为n+1
    }
    int mid = t[p].l + t[p].r >> 1;
    if(x <= mid)change(p*2,x);
    else if(x > mid)change(p*2+1,x);
    t[p].id = max(t[p*2].id,t[p*2+1].id);
}
int query(int p,int l,int r,int x){
    if(t[p].l >= l && t[p].r <= r){//找到被[k,n]完全包含的结点
        if(t[p].l == t[p].r){
            if(t[p].id > x)
                return t[p].l;
            return n + 1;
        }
        if(t[p*2].id > x)return query(p*2,l,r,x);
        if(t[p*2+1].id > x)return query(p*2+1,l,r,x);
        return n + 1;
    }
    int mid = t[p].l + t[p].r >> 1;
    int res = n+1;
    if(mid >= l){
        res = query(p*2,l,r,x);
    }
    if(mid < r){
        res = min(res, query(p*2+1,l,r,x));
    }
    return res;
}
int main(){
    int T;scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)b[i] = 0;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            b[a[i]] = i;//因为时排列,每个a[i]都不一样
        }
        build(1,1,n);
        int res = 0;
        while(m--){
            int op,x,y;
            scanf("%d%d",&op,&x);
            if(op == 1){
                x ^= res;
                change(1,a[x]);
            }
            else{
                scanf("%d",&y);x^=res;y^=res;
                res = query(1,y,n,x);
                printf("%d\n",res);
            }
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/1625--H/p/11404011.html
今日推荐