2019年CCPC - 网络赛B:array【线段树】

题目:

2019中国大学生程序设计竞赛(CCPC)- 网络选拔赛B 

题意:

给定一个1到N的全排列数组,有两种操作:(1)将a[x]的值加1e7,(2)查询一个最小的数x,满足x >= K 且 x 不等于a[1...R]中的任意一个数

分析:

首先对于每个查询,答案一定是[K,N+1]中的一个数,我们只需要在这个区间中找到一个最小的数且这个数的位置大于R即可,怎么做呢?每个数的大小是不超过N且唯一的,那么就建一颗权值线段树,每个节点保存这个区间中的数出现的位置的最大值,查询时根据最大值和R的大小向左右子树走即可;对于修改操作,由于加的值太大,加之后这个值永远不会影响答案了,那就将加之前a[x]的位置修改到大于N就好了,代表a[x]永远是合法的

最后出乎意料的拿到了2个名额  //高兴

代码:

#include <bits/stdc++.h>
 
#define fi first
#define se second
#define pb push_back
#define pii pair<int,int>
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
const int maxn = 2e5+25;
int T,n,m,v,a[maxn],b[maxn],tr[maxn<<2];
void BuildTree(int l,int r,int x){
    if(l == r){
        tr[x] = a[l];
        return ;
    }
    int mid = (l+r)>>1;
    BuildTree(l,mid,x<<1);
    BuildTree(mid+1,r,x<<1|1);
    tr[x] = max(tr[x<<1],tr[x<<1|1]);
}
void UpdataTree(int l,int r,int L,int R,int x,int v){
    if(l > R || r < L) return ;
    if(l >= L && r <= R){
        tr[x] = v;
        return ;
    }
    int mid = (l+r) >> 1;
    UpdataTree(l,mid,L,R,x<<1,v);
    UpdataTree(mid+1,r,L,R,x<<1|1,v);
    tr[x] = max(tr[x<<1],tr[x<<1|1]);
}
void QueryTree(int l,int r,int L,int R,int x,int val,int &ans){
    if(l>R||r<L||ans!=-1) return;
    if(l == r){
        ans = l;
        return ;
    }
    int mid = (l+r)>>1;
    if(tr[x<<1]>val) QueryTree(l,mid,L,R,x<<1,val,ans);
    if(tr[x<<1|1]>val) QueryTree(mid+1,r,L,R,x<<1|1,val,ans);
}
int op,t1,t2,t3;
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d %d",&n,&m);
        for(int i = 1;i <= n; ++i) scanf("%d",&v),a[v]=i,b[i] = v;
        BuildTree(1,n,1); int lastans = 0;
        while(m--){
            scanf("%d",&op);
            if(op == 1){
                scanf("%d",&t1);
                int pos = t1^lastans;
                if(b[pos] <= n)
                UpdataTree(1,n,b[pos],b[pos],1,n+5);
                b[pos] = 1e7;
            }
            else{
                scanf("%d %d",&t2,&t3);
                int ans = -1;
                QueryTree(1,n,t3^lastans,n,1,t2^lastans,ans);
                if(ans == -1) lastans = n+1;
                else lastans = ans;
                printf("%d\n",lastans);
            }
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值