题解:P13977 数列分块入门 2
Chenyichen0420 · · 题解
思路分析
这类题是直接套上分块就能直接看出怎么做的。
显然我们可以先将整个序列分块,然后考虑整块和散块怎么操作。
先考虑查询。对于散块,我们直接暴力判断就行了,对于整块,我们考虑进行二分。这意味着我们还需要维护一个块的有序版本。这部分是修改操作的散块负责维护的。
再考虑修改操作。对于整块,我们直接打上一个整体加的 tag,对于散块,我们直接将要加的部分加一下,然后重构对应块的有序版本。
不难分析出来时间复杂度是
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n, a[500005], l[2300], r[2300], b[500005], op, lp, rp, c, lz[2300], v[500005];
inline void init() {
int sz = sqrt(n * 1.0);
for (int i = 1; i <= sz; ++i) {
l[i] = (i - 1) * sz + 1, r[i] = i * sz;
sort(a + l[i], a + r[i] + 1);
}
if (r[sz] < n) {
sz++; l[sz] = r[sz - 1] + 1, r[sz] = n;
sort(a + l[sz], a + r[sz] + 1);
}
for (int i = 1; i <= sz; ++i)
for (int j = l[i]; j <= r[i]; ++j)
b[j] = i;
}
inline void chg() {
int lt = b[lp], rt = b[rp];
if (lt == rt) {
for (int i = lp; i <= rp; ++i) v[i] += c;
for (int i = l[lt]; i <= r[rt]; ++i) a[i] = v[i];
sort(a + l[lt], a + r[rt] + 1);
return;
}
for (int i = lt + 1; i < rt; ++i) lz[i] += c;
for (int i = lp; i <= r[lt]; ++i) v[i] += c;
for (int i = l[lt]; i <= r[lt]; ++i) a[i] = v[i];
sort(a + l[lt], a + r[lt] + 1);
for (int i = l[rt]; i <= rp; ++i) v[i] += c;
for (int i = l[rt]; i <= r[rt]; ++i) a[i] = v[i];
sort(a + l[rt], a + r[rt] + 1);
}
inline int que() {
int lt = b[lp], rt = b[rp], ans = 0; c *= c;
if (lt == rt) {
for (int i = lp; i <= rp; ++i)
ans += (c > (v[i] + lz[lt]));
return ans;
}
for (int i = lp; i <= r[lt]; ++i)
ans += (c > (v[i] + lz[lt]));
for (int i = l[rt]; i <= rp; ++i)
ans += (c > (v[i] + lz[rt]));
for (int i = lt + 1; i < rt; ++i)
ans += lower_bound(a + l[i], a + r[i] + 1, c - lz[i]) - a - l[i];
return ans;
}
signed main() {
ios::sync_with_stdio(0);
cin >> n;
for (int i = 1; i <= n; ++i) cin >> a[i], v[i] = a[i];
init();
for (int i = 1; i <= n; ++i) {
cin >> op >> lp >> rp >> c;
if (op) cout << que() << endl;
else chg();
}
return 0;
}