主席树+思维:HDU-6703

CCPC网络预选赛的题目,挺有意思的。

题目大意:

给你一个排列.然后q次操作,两种:
1.将一个数 + 1e7.
2.查询不属于 [ 1 , r ] [1,r] [1,r]的且 ≥ k \geq k k的最小值.
强制在线. n , q ≤ 1 e 5 , k ≤ n n,q\leq1e5 , k \leq n n,q1e5,kn

题目思路:

看到操作2应该要自然联想到主席树了。。查询区间大于某个数的最小值.

因为它是一个排列.所以结果最大也就是 n + 1.所以操作一相当于删除一个数.

删除一个数的影响? 考虑一次查询2:r,k.
若 删除的数 ∉ \notin / [ 1 , r ] [1,r] [1,r].那么对最终结果无影响。
若 删除的数 ∈ \in [ 1 , r ] [1,r] [1,r]那么可以将这个被删的数视作在范围 [ r + 1 , n ] [r+1,n] [r+1,n].

所以我们可以维护一个set存当前已经删除的数.然后对 [ r + 1 , n ] [r+1,n] [r+1,n]主席树上找大于等于k的最小值。得到的结果与set中找的结果取最小值即可。

复杂度的思考:

首先说结论: O ( n l o g n ) O(nlogn) O(nlogn)

为什么?
虽然我们是在主席树上搜索。但是无论如何,我们代码的逻辑是在向区间 [ k , k ] [k,k] [k,k]逼近的。

我们可以这么去理解这个过程:先跑到 [ k , k ] [k,k] [k,k]查看是否有答案。然后在返回的过程中,检查每个右子树的sz.若右子树的 sz 不为0.那么就进入右子树。一旦进入右子树,那么答案一定就存在了。所以最差情况,也就遍历两条链。单次复杂度: O ( l o g n ) O(logn) O(logn)

AC代码:

#include<bits/stdc++.h>
using namespace std;
#define mid ((l + r)>>1)
const int maxn = 1e5 + 5;
const int inf = 1e9;
int sum[maxn << 5] , ls[maxn << 5] , rs[maxn << 5] , rt[maxn] , tot;
int add (int l , int r , int  t , int p)
{
    
    
    int now = ++tot;
    ls[now] = ls[t];
    rs[now] = rs[t];
    sum[now] = sum[t] + 1;
    if (l == r) return now;
    if (p <= mid) ls[now] = add(l , mid , ls[now] , p);
    else rs[now] = add(mid + 1 , r , rs[now] , p);
    return now;
}
int ask (int u , int v , int l , int r , int k)
{
    
    
    if (l == r) return l;
    int ans = inf;
    if (sum[ls[v]] - sum[ls[u]]  && mid >= k) ans = ask(ls[u] , ls[v] , l , mid , k);
    if (ans != inf) return ans;
    if (sum[rs[v]] - sum[rs[u]]) ans = ask (rs[u] , rs[v] , mid + 1 , r , k);
    return ans;
}
set<int> S;
int a[maxn];
int main()
{
    
    
    int t;scanf("%d" , &t);
    while( t-- ){
    
    
        tot = 0;
        S.clear();
        int n , m;scanf("%d%d" , &n , &m);
        rt[0] = 0;
        for (int i = 1 ; i <= n ; i++){
    
    
            scanf("%d" , a + i);
            rt[i] = add(1 , n , rt[i - 1] , a[i]);
        }
        int lastans = 0;
        while (m--){
    
    
            int op;
            scanf("%d" , &op);
            if (op == 1){
    
    
                int x;scanf("%d" , &x);
                x ^= lastans;
                S.insert(a[x]);
            }else {
    
    
                int r , k;scanf("%d%d" , &r , &k);
                r ^= lastans;
                k ^= lastans;
            //    cout << "实际为" << r << " " << k << endl;
                int res = ask(rt[r] , rt[n] , 1 , n , k);
                auto it = S.lower_bound(k);
                if (it != S.end())
                    res = min (res , *it);

                if (res == inf) res = n + 1;

                printf("%d\n" , res);
                lastans = res;
            }
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35577488/article/details/109015315
今日推荐