题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6703
题意:现在有一个长度问 n n n的数列,数列里面的数各不相同且都属于 [ 1 , n ] [1,n] [1,n],有 m m m次操作,每次操作包含两种情况,第一种是找一个 p o s pos pos,将第 p o s pos pos个数改成 n u m [ p o s ] + 10 , 000 , 000 num[pos]+10,000,000 num[pos]+10,000,000,第二种是输入两个数 r r r和 k k k,你需要找到一个不小于 k k k的数并且这个数不能和区间 [ 1 , r ] [1,r] [1,r]内的所有数相同,最后此题强制在线。
解题心得:
- 这个题要看明白如果将某个数加上 10 , 000 , 000 10,000,000 10,000,000,可以看成这个数无论在哪里将对 k k k不再有任何限制,明白了这个之后思路就很清晰了,答案可能在两部分中,第一部分是被操作过的数,在这些数中找到第一个不小于 k k k的数,第二部分是在 [ r + 1 , n ] [r+1,n] [r+1,n]中找到第一个不小于 k k k的数,在这两个数中取个最小值。
- 具体实现,对于在 [ r + 1 , n ] [r+1,n] [r+1,n]中找第一个比 k k k大的数可以用主席树解决,对于没有限制的数可以直接用 s e t set set插进去就行了。对于主席树的操作我先找到在区间中小于 k k k的数记为 c n t cnt cnt个,这样我再在区间中找第 c n t + 1 cnt+1 cnt+1个数,这样就找到了第一个大于 k k k的数。
- 刚开始一眼看出了这个题修改的本质,然后用了两个线段树进行xjb维护,然后xjb寻找,就TLE了。想了想这不是主席树吗,但是修改部分怎么办呢,带修主席树??然后莫名发现用个 s e t set set不就把修改的数解决了吗。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 7e5+100;
const ll INF = 1e7;
int n, m, num[maxn], lastans, Index, rt[maxn];
set <int> se;
struct Node {
int cnt, ls, rs;
}node[maxn<<3];
void update(int root) {
node[root].cnt = node[node[root].ls].cnt + node[node[root].rs].cnt;
}
void insert(int root, int root2, int l, int r, int pos) {
node[root] = node[root2];
if(l == r) {
node[root].cnt++;
return ;
}
int mid = l + r >> 1;
if(pos <= mid) {
node[root].ls = ++Index;
insert(node[root].ls, node[root2].ls, l, mid, pos);
} else if(pos > mid) {
node[root].rs = ++Index;
insert(node[root].rs, node[root2].rs, mid+1, r, pos);
}
update(root);
}
void init() {
Index = 0;
memset(node, 0, sizeof(node));
lastans = 0;
memset(rt, 0, sizeof(rt));
se.clear();
scanf("%d%d", &n, &m);
se.insert(n+1);
for(int i=1;i<=n;i++) {
scanf("%d", &num[i]);
if(rt[i] == 0) rt[i] = ++Index;
insert(rt[i], rt[i-1], 1, n+1, num[i]);
}
//这里插入n+1,即k能取的最大值
rt[n+1] = ++Index;
insert(rt[n+1], rt[n], 1, n+1, n+1);
n++;
lastans = 0;
}
int get_cnt(int root, int root2, int l, int r, int ql, int qr) {//找到区间内有多少个小于k的数
if(ql > qr) return 0;
int mid = l + r >> 1;
if(l == ql && r == qr) {
return node[root2].cnt - node[root].cnt;
}
if(qr <= mid) return get_cnt(node[root].ls, node[root2].ls, l, mid, ql, qr);
else if(ql > mid) return get_cnt(node[root].rs, node[root2].rs, mid+1, r, ql, qr);
else {
return get_cnt(node[root].ls, node[root2].ls, l, mid, ql, mid) +
get_cnt(node[root].rs, node[root2].rs, mid+1, r, mid+1, qr);
}
}
int query(int root, int root2, int l, int r, int x) {
if(l == r) {
return l;
}
int chl1 = node[root].ls, chl2 = node[root2].ls;
int cnt1 = node[chl2].cnt - node[chl1].cnt;
int mid = l + r >> 1;
if(cnt1 < x) {
return query(node[root].rs, node[root2].rs, mid+1, r, x - cnt1);
} else {
return query(node[root].ls, node[root2].ls, l, mid, x);
}
}
int main() {
// freopen("1.in.txt", "r", stdin);
int t; scanf("%d", &t);
while(t--) {
init();
for (int i = 1; i <= m; i++) {
int op;
scanf("%d", &op);
if (op == 1) {
int pos;
scanf("%d", &pos);
pos = pos ^ lastans;
se.insert(num[pos]);
} else {
int r, k;
scanf("%d%d", &r, &k);
r = r ^ lastans;
k = k ^ lastans;
int cnt = get_cnt(rt[r], rt[n], 1, n, 1, k-1);
int ans1 = INF, ans2;
if(cnt < (n-r+1))
ans1 = query(rt[r], rt[n], 1, n, cnt+1);
ans2 = (*se.lower_bound(k));
int ans = min(ans1, ans2);
lastans = ans;
printf("%d\n", ans);
}
}
}
return 0;
}