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,q≤1e5,k≤n
题目思路:
看到操作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;
}