hdu6703array(权值线段树)

题意:t 组样例,每组给出 n,m(n<100000), n个数 从1到n顺序不定,m次询问。 格式为 (1,t1)或(2,t2,t3)

1 操作 t1 = t1^lastans.  把 a[t1] 的值 加 10000000,

2 操作 t2^=lastans, t3^=lastans,  问 1到 t2 区间没有出现过且不小于 t3 的最小数是多少。

思路:数组中的值唯一,且在1到n的范围内,而询问的r和k也在1到n的范围内。 所以对于任意一个被操作1修改过的值都不会成为询问的答案,而询问的结果也必然在k到n+1的范围内。 因为没有被修改过值是唯一的,所以可以建立权值线段树,维护权值区间内的值所在下标的最大值。而询问则转化为不小于k的值里面,下标超过r的最小权值是多少。 如何处理询问呢,一种较为暴力的解法是直接在线段树上询问权值在k到n+1的范围内第一个下标超过r的权值是多少。但复杂度可能会被卡,需要减枝。 再加上一个额外的判断就可以了,就是在递归查询完左子树内存不存在大于r的下标之后,如果不存在,则先看一下右子树内的下标的最大值是否大于r。如果不大于r,则不必再进入右子树内查询,否则答案一定在右子树内。在进左子树之前也利用同样的判断条件来判断是否有必要进入左子树,这样做可以保证单次查询的复杂度是O(log n) 的。 而对于操作1,则等价于修改某个权值的下标为无穷大。操作复杂度也是O(log n)的。 综上所述,得到了一个复杂度为O( m * log n )的在线算法,可以较快地通过此题。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5 + 10;
int t, n, m, a[N], pre[N], tree[N << 2];
void pushup(int rt) {
    tree[rt] = max(tree[rt << 1], tree[rt << 1 | 1]);
}
void build(int l, int r, int rt) {
    if(l == r) {
        tree[rt] = pre[l];
        return;
    }
    int m = (l + r) >> 1;
    build(l, m, rt << 1);
    build(m + 1, r, rt << 1 | 1);
    pushup(rt);
}
void update(int pos, int num, int l, int r, int rt) {
    if(l == r) {
        tree[rt] = num;
        return;
    }
    int m = (l + r) >> 1;
    if(pos <= m)
        update(pos, num, l, m, rt << 1);
    else
        update(pos, num, m + 1, r, rt << 1 | 1);
    pushup(rt);
}
int query(int R, int k, int l, int r, int rt) {
    if(l == r && tree[rt] > R && k <= r)
        return l;
    int m = (l + r) >> 1;
    if(k <= m && tree[rt << 1] > R) {
        int d = query(R, k, l, m, rt << 1);
        if(d != n + 1)
            return d;
    }
    if(k <= r && tree[rt << 1 | 1] > R) {
        int d = query(R, k, m + 1, r, rt << 1 | 1);
        if(d != n + 1)
            return d;
    }
    return n + 1;
}
int main() {
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            pre[a[i]] = i;
        }
        build(1, n, 1);
        int lastans = 0, opt, x, y;
        while(m--) {
            scanf("%d", &opt);
            if(opt == 1) {
                scanf("%d", &x);
                x ^= lastans;
                update(a[x], N + 100, 1, n, 1);
            } else {
                scanf("%d%d", &x, &y);
                x ^= lastans;
                y ^= lastans;
                lastans = query(x, y, 1, n, 1);
                printf("%d\n", lastans);
            }
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值