抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

P2617
单点修改,区间查询第 kk 小。

树套树

区间第 kk 小的一种解法是用主席树,利用两个子树做差实现。
但加上单点修改后,直接做每次修改是 O(nlogn)O(n\log n) 的,复杂度太大。

考虑平衡询问和修改的复杂度。
我们在主席树外套一棵树状数组
询问时把两个子树作差变成 logn\log n 个子树作差。
修改时只需要修改 logn\log n 个子树即可。

时间复杂度:O(nlog2n)O(n \log^2 n)
空间复杂度:O(nlogn)O(n \log n)

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define FL(a,b,c) for(int a=(b),a##end=(c);a<=a##end;++a)
#define FR(a,b,c) for(int a=(b),a##end=(c);a>=a##end;--a)
#define lowbit(x) ((x)&-(x))
#define eb emplace_back
#define sz(x) (int)((x).size())
#define int long long
#define vt vector
#define fr first
#define se second
#define mmt(x, y) memset(x, y, sizeof x)
#define PII pair<int, int>
#define max(a, b)({auto f7r=(a);auto j3h=(b);f7r<j3h?j3h:f7r;})
#define cmax(a, b)({auto j3h=(b);(j3h>a)&&(a=j3h);})
#define min(a, b)({auto f7r=(a);auto j3h=(b);f7r>j3h?j3h:f7r;})
#define cmin(a, b)({auto j3h=(b);(j3h<a)&&(a=j3h);})
constexpr int N = 1e5 + 10;
int n, m, D[N << 1], a[N], tot, len, rt[N];
struct A{int l, r, k;}Q[N];
struct B{int ls, rs, s;}sg[N * 400];
void modify(int&u, int l, int r, int pos, int v){
if(!u)u = ++tot;
if(l == r)return void(sg[u].s += v);
int mid = l + r >> 1;
if(pos <= mid)modify(sg[u].ls, l, mid, pos, v);
else modify(sg[u].rs, mid + 1, r, pos, v);
sg[u].s = sg[sg[u].ls].s + sg[sg[u].rs].s;
}
void modify(int pos, int v){
int k = lower_bound(D + 1, D + 1 + len, a[pos]) - D, i = pos;
while(i <= n)modify(rt[i], 1, len, k, v), i += lowbit(i);
}
int tmp[2][30], cnt[2];
int query(int l, int r, int k){
if(l == r)return l;
int mid = l + r >> 1, s = 0;
FL(i, 1, cnt[1])s += sg[sg[tmp[1][i]].ls].s;
FL(i, 1, cnt[0])s -= sg[sg[tmp[0][i]].ls].s;
FL(i, 1, cnt[1])tmp[1][i] = (k > s ? sg[tmp[1][i]].rs : sg[tmp[1][i]].ls);
FL(i, 1, cnt[0])tmp[0][i] = (k > s ? sg[tmp[0][i]].rs : sg[tmp[0][i]].ls);
return (k <= s) ? query(l, mid, k) : query(mid + 1, r, k - s);
}
int ask(int l, int r, int k){
mmt(tmp, 0), cnt[0] = cnt[1] = 0;
while(r)tmp[1][++cnt[1]] = rt[r], r -= lowbit(r);
while(l)tmp[0][++cnt[0]] = rt[l], l -= lowbit(l);
return query(1, len, k);
}
int32_t main(){
cin.tie(0)->sync_with_stdio(0);
cin >> n >> m;
FL(i, 1, n)cin >> a[i], D[++len] = a[i];
FL(i, 1, m){
char op;
cin >> op;
if(op == 'Q')cin >> Q[i].l >> Q[i].r >> Q[i].k;
else Q[i].l = 0, cin >> Q[i].r >> Q[i].k, D[++len] = Q[i].k;
}
sort(D + 1, D + 1 + len), len = unique(D + 1, D + 1 + len) - D - 1;
FL(i, 1, n)modify(i, 1);
FL(i, 1, m){
if(Q[i].l)cout << D[ask(Q[i].l - 1, Q[i].r, Q[i].k)] << endl;
else modify(Q[i].r, -1), a[Q[i].r] = Q[i].k, modify(Q[i].r, 1);
}
return 0;
}

分块

先对序列分块,再对值域分块。
我们记两个数组:

  1. s1i,js1_{i,j} 为前 ii 块中值域在第 jj 个的数的数量。
  2. s2i,js2_{i,j} 为前 ii 块中 jj 出现的数量。

预处理复杂度 O(nn)O(n\sqrt n)

每次查询先找到答案所在的块,再找到答案即可。
修改只是单点,所以直接暴力。

这种做法可以推广到区间修改,例题

时间复杂度:O(nn)O(n\sqrt n)
空间复杂度:O(nn)O(n\sqrt n)

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define FL(a,b,c) for(int a=(b),a##end=(c);a<=a##end;++a)
#define FR(a,b,c) for(int a=(b),a##end=(c);a>=a##end;--a)
#define lowbit(x) ((x)&-(x))
#define eb emplace_back
#define sz(x) (int)((x).size())
#define int long long
#define vt vector
#define fr first
#define se second
#define mmt(x, y) memset(x, y, sizeof x)
#define PII pair<int, int>
#define max(a, b)({auto f7r=(a);auto j3h=(b);f7r<j3h?j3h:f7r;})
#define cmax(a, b)({auto j3h=(b);(j3h>a)&&(a=j3h);})
#define min(a, b)({auto f7r=(a);auto j3h=(b);f7r>j3h?j3h:f7r;})
#define cmin(a, b)({auto j3h=(b);(j3h<a)&&(a=j3h);})
constexpr int N = 1e5 + 10, M = 400;
int n, m, D[N * 2], a[N], len, L[M], R[M], b1, b2;
int s1[M][M], s2[M][N * 2], id1[N], id2[N * 2], t1[M], t2[N * 2];
struct A{int l, r, k;}Q[N];
int query(int l, int r, int k){
if(id1[l] == id1[r]){
FL(i, l, r)t2[i] = a[i];
nth_element(t2 + l, t2 + l + k - 1, t2 + r + 1);
int ans = t2[l + k - 1];
FL(i, l, r)t2[i] = 0;
return ans;
}
int s;
FL(i, l, R[id1[l]])t1[id2[a[i]]]++, t2[a[i]]++;
FR(i, r, L[id1[r]])t1[id2[a[i]]]++, t2[a[i]]++;
FL(i, 1, id2[len]){
if(k > (s = t1[i] + s1[id1[r] - 1][i] - s1[id1[l]][i]))k -= s;
else{
FL(j, (i - 1) * b2 + 1, i * b2){
if(k > (s = t2[j] + s2[id1[r] - 1][j] - s2[id1[l]][j]))k -= s;
else{
FL(i, l, R[id1[l]])t1[id2[a[i]]]--, t2[a[i]]--;
FR(i, r, L[id1[r]])t1[id2[a[i]]]--, t2[a[i]]--;
return j;
}
}
}
}
return 0;
}
void modify(int pos, int k){
FL(i, id1[pos], id1[n])
s1[i][id2[a[pos]]]--, s2[i][a[pos]]--,
s1[i][id2[k]]++, s2[i][k]++;
a[pos] = k;
}
int32_t main(){
cin.tie(0)->sync_with_stdio(0);
cin >> n >> m;
FL(i, 1, n)cin >> a[i], D[++len] = a[i];
FL(i, 1, m){
char op;cin >> op;
if(op == 'Q')cin >> Q[i].l >> Q[i].r >> Q[i].k;
else Q[i].l = 0, cin >> Q[i].r >> Q[i].k, D[++len] = Q[i].k;
}
sort(D + 1, D + 1 + len), len = unique(D + 1, D + 1 + len) - D - 1;
b1 = sqrt(n), b2 = sqrt(len);
FL(i, 1, n)id1[i] = (i - 1) / b1 + 1, a[i] = lower_bound(D + 1, D + 1 + len, a[i]) - D;
FL(i, 1, len)id2[i] = (i - 1) / b2 + 1;
FL(i, 1, id1[n]){
L[i] = R[i - 1] + 1, R[i] = min(L[i] + b1 - 1, n);
FL(j, 1, id2[len])s1[i][j] = s1[i - 1][j];
FL(j, 1, len)s2[i][j] = s2[i - 1][j];
FL(j, L[i], R[i])s1[i][id2[a[j]]]++, s2[i][a[j]]++;
}
FL(i, 1, m){
if(Q[i].l)cout << D[query(Q[i].l, Q[i].r, Q[i].k)] << endl;
else modify(Q[i].r, lower_bound(D + 1, D + 1 + len, Q[i].k) - D);
}
return 0;
}

整体二分

将同一个范围内的答案统一处理,将多个询问一起解决。
利用分治将问题转化成判定,并不断地将答案取值区间二分。
然后把询问分成两部分,然后在递归至底部后获得一批询问的答案。

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define FL(a,b,c) for(int a=(b),a##end=(c);a<=a##end;++a)
#define FR(a,b,c) for(int a=(b),a##end=(c);a>=a##end;--a)
#define lowbit(x) ((x)&-(x))
#define eb emplace_back
#define sz(x) (int)((x).size())
#define int long long
#define vt vector
#define fr first
#define se second
#define mmt(x, y) memset(x, y, sizeof x)
#define PII pair<int, int>
#define max(a, b)({auto f7r=(a);auto j3h=(b);f7r<j3h?j3h:f7r;})
#define cmax(a, b)({auto j3h=(b);(j3h>a)&&(a=j3h);})
#define min(a, b)({auto f7r=(a);auto j3h=(b);f7r>j3h?j3h:f7r;})
#define cmin(a, b)({auto j3h=(b);(j3h<a)&&(a=j3h);})
constexpr int N = 5e5 + 10;
struct A{int op, id, k, l, r;}v[N], P[N], Q[N];
int n, m, cnt, D[N], len, a[N], ans[N], w[N];
void add(int x, int y){while(x <= len)w[x] += y, x += lowbit(x);}
int query(int x){int ans = 0;while(x)ans += w[x], x -= lowbit(x);return ans;}
void solve(int l, int r, int L, int R){
if(L == R){
FL(i, l, r)if(!v[i].op)ans[v[i].id] = L;
return;
}
int mid = L + R >> 1, p = 0, q = 0;
FL(i, l, r)
if(v[i].op){
if(v[i].k <= mid)add(v[i].id, v[i].op), P[++p] = v[i];
else Q[++q] = v[i];
}
else{
int cnt = query(v[i].r) - query(v[i].l - 1);
if(cnt >= v[i].k)P[++p] = v[i];
else Q[++q] = v[i], Q[q].k -= cnt;
}
FL(i, l, r)if(v[i].op && v[i].k <= mid)add(v[i].id, -v[i].op);
FL(i, 1, p)v[i + l - 1] = P[i];
FL(i, 1, q)v[i + l + p - 1] = Q[i];
if(p)solve(l, l + p - 1, L, mid);
if(q)solve(r - q + 1, r, mid + 1, R);
}
int32_t main(){
cin.tie(0)->sync_with_stdio(0);
cin >> n >> m;
int x;
FL(i, 1, n)cin >> x, v[++cnt] = {1, i, x}, D[++len] = x, a[i] = x;
FL(i, 1, m){
char op;int x, y, z;
cin >> op >> x >> y;
if(op == 'Q')cin >> z, v[++cnt] = {0, i, z, x, y};
else v[++cnt] = {-1, x, a[x]}, v[++cnt] = {1, x, (a[x] = y)}, D[++len] = y;
}
sort(D + 1, D + 1 + len), len = unique(D + 1, D + 1 + len) - D - 1;
FL(i, 1, cnt)if(v[i].op)v[i].k = lower_bound(D + 1, D + 1 + len, v[i].k) - D;
solve(1, cnt, 1, len);
FL(i, 1, m)if(ans[i])cout << D[ans[i]] << endl;
return 0;
}