Description
给定序列 a = ( a 1 , a 2 , ⋯ , a n ) a=(a_1,a_2,\cdots,a_n) a=(a1,a2,⋯,an),有 m m m 个操作分两种:
- replace ( l , r , x , y ) \operatorname{replace}(l,r,x,y) replace(l,r,x,y):将 a l ∼ a r a_l\sim a_r al∼ar 中的所有 x x x 替换为 y y y.
- kth ( l , r , k ) \operatorname{kth}(l,r,k) kth(l,r,k):求 a l ∼ a r a_l\sim a_r al∼ar 中的第 k k k 小值.
Limitations
1
≤
n
,
m
,
a
i
,
x
,
y
≤
10
5
1\le n,m,a_i,x,y\le 10^5
1≤n,m,ai,x,y≤105
1
≤
l
≤
r
≤
n
1\le l\le r\le n
1≤l≤r≤n
1
s
,
512
MB
\textcolor{red}{1\text{s}},512\text{MB}
1s,512MB
Solution
很奇怪的操作,考虑给序列
a
a
a 分块.
由于要查询第
k
k
k 小,再给值域
[
1
,
10
5
]
[1,10^5]
[1,105] 分块.
预处理两个数组:
- f i , j f_{i,j} fi,j:第 1 ∼ i 1\sim i 1∼i 个序列块中, j j j 的出现次数.
- g i , j g_{i,j} gi,j:第 1 ∼ i 1\sim i 1∼i 个序列块中,第 j j j 个值域块内数的的出现次数.
并在每块挂上一个 vector
存储该块的操作,重构块时,遍历 vector
计算出
to
i
\textit{to}_i
toi 表示
i
i
i 这个数值最终会变成什么,然后直接在
a
a
a 上修改,并把 vector
清空.
考虑修改,对于整块,我们直接把操作塞入对应块的 vector
中;对于散块,我们暴力替换.
当然
f
f
f 和
g
g
g 也要更新,所以替换时需要记录
t
i
t_i
ti 表示第
i
i
i 块内的替换次数,在所有块修改完后更新这两个数组.
接着考虑查询,我们先对每个
i
i
i 求出第
i
i
i 个值域块内数在
a
l
∼
a
r
a_l\sim a_r
al∼ar 的出现次数,然后定出第
k
k
k 小值所在的值域块.
接着再求出数值
i
i
i 在
a
l
∼
a
r
a_l\sim a_r
al∼ar 的出现次数,再在上一步确定的值域块内找出真正的第
k
k
k 小值.
此时
f
f
f 和
g
g
g 就发挥了作用,因为要求出连续整块内一个数(或一个值域块内数)的出现次数.(散块就直接暴力装桶)
至此就想完了,然而本题很毒瘤(又难写又卡常),注意以下几点:
- 块长取 350 350 350 左右,太小会爆空间.
- 注意区分序列块和值域块.
- x = y x=y x=y 的修改直接忽略.
- 不要开太多
vector
,要加快读快写. - 注意优化清空过程.
Code
4.65
KB
,
5.78
s
,
204.49
MB
(in
total,
C++20
with
O2)
4.65\text{KB},5.78\text{s},204.49\text{MB}\;\texttt{(in total, C++20 with O2)}
4.65KB,5.78s,204.49MB(in total, C++20 with O2)
常数很大,最大点要
0.81
s
\textcolor{red}{0.81\text{s}}
0.81s.
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;
template<class T>
bool chmax(T &a, const T &b){
if(a < b){ a = b; return true; }
return false;
}
template<class T>
bool chmin(T &a, const T &b){
if(a > b){ a = b; return true; }
return false;
}
namespace fastio {}
using fastio::read;
using fastio::write;
constexpr int B = 350, V = 1e5 + 10, M = (V + B - 1) / B;
using pii = pair<int, int>;
namespace pool {
constexpr int L = 4e7 + 10;
int P[L], *ptr = P;
inline int* alloc(int n) {
int* res = ptr;
ptr += n;
return res;
}
}
using pool::alloc;
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
const int n = read<int>(), q = read<int>();
const int blocks = (n + B - 1) / B;
int* a = alloc(n);
for (int i = 0; i < n; i++) a[i] = read<int>();
int *bel = alloc(V), *to = alloc(V), *L = alloc(blocks), *R = alloc(blocks), *tmp = alloc(V);
vector<int*> f(blocks), g(blocks);
vector<vector<pii>> mdf(blocks);
for (int i = 0; i < blocks; i++) f[i] = alloc(V), g[i] = alloc(M);
auto init = [&]() {
for (int i = 0; i < V; i++) {
bel[i] = i / B;
to[i] = i;
}
for (int i = 0; i < blocks; i++) {
L[i] = i * B;
R[i] = min(L[i] + B, n) - 1;
if (i > 0) {
for (int j = 0; j < V; j++) f[i][j] = f[i - 1][j];
for (int j = 0; j < M; j++) g[i][j] = g[i - 1][j];
}
for (int j = L[i]; j <= R[i]; j++) {
f[i][a[j]]++;
g[i][bel[a[j]]]++;
}
}
};
vector<int> vec;
vec.reserve(q << 1);
auto refact = [&](int b) {
vec.clear();
vector<pii> & opr = mdf[b];
reverse(opr.begin(), opr.end());
for (auto & [x, y] : opr) {
to[x] = to[y];
vec.push_back(x);
vec.push_back(y);
}
for (int i = L[b]; i <= R[b]; i++) a[i] = to[a[i]];
for (auto x : vec) to[x] = x;
opr.clear();
};
auto brute_replace = [&](int b, int l, int r, int x, int y) {
refact(b);
for (int i = l; i <= r; i++)
if (a[i] == x) {
a[i] = y;
tmp[b]++;
}
};
auto block_replace = [&](int b, int x, int y) {
mdf[b].emplace_back(x, y);
tmp[b] = f[b][x] - f[b - 1][x];
};
auto update = [&](int b, int x, int y) {
for (int i = b; i < blocks; i++) {
if (i > 0) tmp[i] += tmp[i - 1];
f[i][x] -= tmp[i], f[i][y] += tmp[i];
g[i][bel[x]] -= tmp[i], g[i][bel[y]] += tmp[i];
}
};
auto replace = [&](int l, int r, int x, int y) {
if (x == y) return;
const int bl = bel[l], br = bel[r];
for (int i = 0; i < blocks; i++) tmp[i] = 0;
if (bl == br) brute_replace(bl, l, r, x, y);
else {
brute_replace(bl, l, R[bl], x, y);
brute_replace(br, L[br], r, x, y);
for (int i = bl + 1; i < br; i++) block_replace(i, x, y);
}
update(bl, x, y);
};
auto _push = [&](int l, int r, int t) {
for (int i = l; i <= r; i++) {
if (t == 0) tmp[bel[a[i]]]++;
else tmp[a[i]]++;
}
};
auto push = [&](int l, int r, int bl, int br, int t) {
if (bl == br) _push(l, r, t);
else _push(l, R[bl], t), _push(L[br], r, t);
};
auto kth = [&](int l, int r, int k) -> int {
const int bl = bel[l], br = bel[r];
refact(bl);
if (bl ^ br) refact(br);
for (int i = 0; i < M; i++) tmp[i] = (bl ^ br) ? (g[br - 1][i] - g[bl][i]) : 0;
push(l, r, bl, br, 0);
int sum = 0, blk = -1;
for (int i = 0; i < M; i++) {
sum += tmp[i];
if (sum >= k) {
blk = i;
sum -= tmp[i];
break;
}
}
if (blk == -1) return -1;
const int fl = blk * B, fr = min(fl + B, V) - 1;
for (int i = fl; i <= fr; i++) tmp[i] = (bl ^ br) ? (f[br - 1][i] - f[bl][i]) : 0;
push(l, r, bl, br, 1);
for (int i = fl; i <= fr; i++) {
sum += tmp[i];
if (sum >= k) return i;
}
return -1;
};
init();
for (int i = 0, op, l, r, x, y; i < q; i++) {
op = read<int>(), l = read<int>(), r = read<int>(), x = read<int>();
l--, r--;
if (op == 1) y = read<int>(), replace(l, r, x, y);
else {
write(kth(l, r, x));
putchar_unlocked('\n');
}
}
return 0;
}