
P5356 [Ynoi Easy Round 2017] 由乃打扑克 Solution
P5356 [Ynoi Easy Round 2017] 由乃打扑克 题解
Description
给定序列 a = ( a 1 , a 2 , ⋯ , a n ) a=(a_1,a_2,\cdots,a_n) a=(a1,a2,⋯,an),有 m m m 个操作分两种:
- add ( l , r , x ) \operatorname{add}(l,r,x) add(l,r,x):对每个 i ∈ [ l , r ] i\in[l,r] i∈[l,r] 执行 a i ← a i + x a_i\gets a_i+x ai←ai+x.
- kth ( l , r , k ) \operatorname{kth}(l,r,k) kth(l,r,k):求 a l ⋯ r a_{l\cdots r} al⋯r 中第 k k k 小的值,无解输出 − 1 -1 −1.
Limitations
1
≤
n
,
m
≤
1
0
5
1\le n,m\le 10^5
1≤n,m≤105
∣
a
i
∣
,
∣
x
∣
≤
2
×
1
0
4
|a_i|,|x| \le 2\times 10^4
∣ai∣,∣x∣≤2×104
2
s
,
128
MB
2\text{s},128\text{MB}
2s,128MB
Solution
看到这种很难维护的操作,考虑分块,每块进行升序排序后存入
p
i
p_i
pi.
先考虑
kth
\operatorname{kth}
kth,先取
a
l
⋯
r
a_{l\cdots r}
al⋯r 中的最小值和最大值.
若
k
=
1
k=1
k=1,答案为最小值,若
k
=
r
−
l
+
1
k=r-l+1
k=r−l+1,答案为最大值,若超出范围则为
−
1
-1
−1,否则以最小值和最大值为左右端点,进行二分.
对于
check
\operatorname{check}
check,求出区间内不大于
mid
\textit{mid}
mid 的数的个数,具体就是散块暴力,整块在
p
i
p_i
pi 上二分,外层二分出的结果就是答案.
再考虑
add
\operatorname{add}
add,显然是整块打上
tag
\textit{tag}
tag,散块重排.
需要注意几点:
- 计算时不要漏掉标记.
- 需要开
long long
,否则会被hack
. - 重排时不要直接
sort
,要分成两组(加过的和没加过的)再归并. - 加快读快写, B ∈ [ 200 , 300 ] B\in[200,300] B∈[200,300] 最优.
Code
4.48
KB
,
11.20
s
,
3.92
MB
(in
total,
C++20
with
O2)
4.48\text{KB},11.20\text{s},3.92\text{MB}\;\texttt{(in total, C++20 with O2)}
4.48KB,11.20s,3.92MB(in total, C++20 with O2)
代码中
B
B
B 取
256
256
256.
#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;
}
// Fast-io
#ifdef LOCAL
#define getchar_unlocked getchar
#define putchar_unlocked putchar
#endif
template<class T>
inline T read() {
T x = 0, f = 1;
char ch = getchar_unlocked();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar_unlocked();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar_unlocked();
}
return x * f;
}
template<class T>
inline void write(T x) {
if (x < 0) {
putchar_unlocked('-');
x = -x;
}
if (x > 9) write(x / 10);
putchar_unlocked(x % 10 + '0');
return;
}
constexpr int B = 256; // Size of each block.
constexpr i64 inf = 6e18;
struct Node {
i64 val;
int pos;
inline Node() {}
inline Node(i64 _val, int _pos) : val(_val), pos(_pos) {}
bool operator<(const Node& rhs) const { return val < rhs.val; }
};
struct Block {
int n, blocks;
vector<i64> a, tag;
vector<int> belong, L, R;
vector<Node> p, t1, t2;
inline Block() {}
inline Block(const vector<i64>& _a) : a(_a) {
n = a.size();
blocks = (n + B - 1) / B;
p.resize(n), belong.resize(n);
L.resize(blocks), R.resize(blocks), tag.resize(blocks);
// Build blocks
for (int i = 0; i < blocks; i++) {
L[i] = i * B;
R[i] = min(L[i] + B, n) - 1;
for (int j = L[i]; j <= R[i]; j++) {
belong[j] = i;
p[j] = Node(a[j], j);
}
sort(p.begin() + L[i], p.begin() + R[i] + 1); // Sort for each block.
}
}
// Find [min, max] of range [l, r].
inline pair<i64, i64> bound(int l, int r) {
int bl = belong[l], br = belong[r];
i64 vl = inf, vr = -inf;
if (bl == br) {
for (int i = l; i <= r; i++) {
vl = min(vl, a[i] + tag[bl]);
vr = max(vr, a[i] + tag[bl]);
}
}
else {
for (int i = l; i <= R[bl]; i++) {
vl = min(vl, a[i] + tag[bl]);
vr = max(vr, a[i] + tag[bl]);
}
for (int i = L[br]; i <= r; i++) {
vl = min(vl, a[i] + tag[br]);
vr = max(vr, a[i] + tag[br]);
}
for (int i = bl + 1; i < br; i++) {
vl = min(vl, p[L[i]].val + tag[i]);
vr = max(vr, p[R[i]].val + tag[i]);
}
}
return {vl, vr};
}
// Find the number of elements LEQ k in range [l, r].
inline int count(int l, int r, int k) {
int res = 0, bl = belong[l], br = belong[r];
if (bl == br) {
for (int i = l; i <= r; i++) res += (a[i] + tag[bl] <= k);
}
else {
for (int i = l; i <= R[bl]; i++) res += (a[i] + tag[bl] <= k);
for (int i = L[br]; i <= r; i++) res += (a[i] + tag[br] <= k);
for (int i = bl + 1; i < br; i++) {
int vl = L[i], vr = R[i];
if (p[vl].val + tag[i] > k) continue;
if (p[vr].val + tag[i] <= k) {
res += vr - vl + 1;
continue;
}
while (vl < vr) {
int mid = ((vl + vr) >> 1) + 1;
if (p[mid].val + tag[i] <= k) vl = mid;
else vr = mid - 1;
}
if (p[vl].val + tag[i] <= k) res += vl - L[i] + 1;
}
}
return res;
}
// Find the k-th smallest number in range [l, r].
inline i64 kth(int l, int r, int k) {
auto [vl, vr] = bound(l, r);
const int len = r - l + 1;
if (k == 1) return vl;
if (k == len) return vr;
if (k < 1 || k > len) return -1;
i64 res = -1;
while (vl <= vr) {
i64 mid = (vl + vr) >> 1;
if (count(l, r, mid) < k) vl = mid + 1;
else vr = (res = mid) - 1;
}
return res;
}
// Resort the b-th block when adding k to range [l, r].
inline void msort(int l, int r, int b, int k) {
t1.clear(), t2.clear();
for(int i = L[b]; i <= R[b]; i++) {
if (i >= l && i <= r) a[i] += k;
if (p[i].pos >= l && p[i].pos <= r) t1.push_back(Node(p[i].val + k, p[i].pos));
else t2.push_back(p[i]);
}
int lp = 0, rp = 0, ptr = L[b];
while (ptr <= R[b]) {
if (lp < t1.size() && (rp >= t2.size() || t1[lp].val < t2[rp].val)) p[ptr++] = t1[lp++];
else p[ptr++] = t2[rp++];
}
}
// Add k to range [l, r].
inline void add(int l, int r, int k) {
int bl = belong[l], br = belong[r];
msort(l, r, bl, k); // Leftmost block
if (bl != br) {
msort(l, r, br, k); // Rightmost block
for (int i = bl + 1; i < br; i++) tag[i] += k;
}
}
};
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
const int n = read<int>(), m = read<int>();
vector<i64> a(n);
for (int i = 0; i < n; i++) a[i] = read<i64>();
Block blk(a);
for (int i = 0, op, l, r, k; i < m; i++) {
op = read<int>(), l = read<int>(), r = read<int>(), k = read<int>();
l--, r--;
if (op == 1) write(blk.kth(l, r, k)), putchar_unlocked('\n');
else blk.add(l, r, k);
}
return 0;
}
更多推荐
所有评论(0)