【权值线段树】HDU6703-array

传送门

题意是说给定一个排列,长度1e5,要求支持两种操作:
1. 把某个位置的数字+1e7
2. 给定r, k.查询与前r个数字不同的,并且大于等于k的,最小的数字。

经过分析,答案最大不可能超过n+1。这样的话,操作1其实相当于把这个数字从排列中删去。

用两棵权值线段树,每个点上存的是位置。

对每次2操作,在[k,n]区间里找最靠左的并且val[x]大于r的数。

对每次1操作,最开始是初始化一颗每个点值都是n+1的权值线段树,删掉一个数代表这个数字在区间内可以被使用了。所以在线段树上把点改为该位置。

同时2操作也要找[k,n]区间里最靠左而且val2[x]小于等于r的数。

两个数取最小就是答案。

同时还需记录区间最大最小值,以用来在查询的时候剪枝。

没有这个剪枝,查询会退化为n^2log(n)的,因为区间里可能每个数都会递归到底。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
#define ls(x) (x << 1)
#define rs(x) ((x << 1) + 1)
#define min(a,b) (a)<(b)?(a):(b)
const int maxn = 1e5 + 233;

struct Node{
    int l, r, v;
}t1[maxn * 4], t2[maxn * 4];
int a[maxn], pos[maxn];
int n;
void build(int p, int l, int r)
{
    if(l == r)
    {
        t1[p].l = l, t1[p].r = r, t1[p].v = n + 1;
        t2[p].l = l, t2[p].r = r, t2[p].v = pos[l];
        return ;
    }
    t1[p].l = t2[p].l = l, t1[p].r = t2[p].r = r;
    int mid = (l + r) >> 1;
    build(ls(p), l, mid); build(rs(p), mid + 1, r);
    t1[p].v = min(t1[ls(p)].v, t1[rs(p)].v);
    t2[p].v = max(t2[ls(p)].v, t2[rs(p)].v);
}
void update(int p, int l, int v)
{
    if(t1[p].l == t1[p].r)
    {
        t1[p].v = v;
        return;
    }
    int mid = (t1[p].l + t1[p].r) >> 1;
    if(l <= mid) update(ls(p), l, v);
    else update(rs(p), l, v);
    t1[p].v = min(t1[ls(p)].v, t1[rs(p)].v);
}
int ask1(int p, int l, int r, int v)
{
    if(t1[p].l == t1[p].r)
    {
        if(t1[p].v <= v) return t1[p].l;
        else return n + 1;
    }
    if(l <= t1[p].l && r >= t1[p].r)
    {
        if(t1[p].v > v) return n + 1;
    }
    int mid = (t1[p].l + t1[p].r) >> 1;
    if(r <= mid) return ask1(ls(p), l, r, v);
    if(l > mid) return ask1(rs(p), l, r, v);
    int val = ask1(ls(p), l, r, v);
    if(val != n + 1) return val;
    return ask1(rs(p), l, r, v);
}
int ask2(int p, int l, int r, int v)
{
    if(t2[p].l == t2[p].r)
    {
        if(t2[p].v >= v) return t2[p].l;
        else return n + 1;
    }
    if(l <= t2[p].l && r >= t2[p].r)
    {
        if(t2[p].v < v) return n + 1;
    }
    int mid = (t2[p].l + t2[p].r) >> 1;
    if(r <= mid) return ask2(ls(p), l, r, v);
    if(l > mid) return ask2(rs(p), l, r, v);
    int val = ask2(ls(p), l, r, v);
    if(val != n + 1) return val;
    return ask2(rs(p), l, r, v);
}
int main()
{
    int T; cin >> T;
    while(T--)
    {
        int m;
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]), pos[a[i]] = i;
        build(1, 1, n);
        int ans = 0;
        int o, r, k; 
        while(m--)
        {
            scanf("%d", &o);
            if(o == 1)
            {
                scanf("%d", &k);
                k ^= ans;
                update(1, a[k], k);
            }
            else
            {
                scanf("%d%d", &r, &k);
                r ^= ans, k ^= ans;
                ans = ask1(1, k, n, r);
                if(r < n) ans = min(ans, ask2(1, k, n, r + 1));
                printf("%d\n", ans);
            }
        }
    }
}

发表评论

邮箱地址不会被公开。 必填项已用*标注