序列:思路比较典的扫描线题。
一个经典 trick:对于涉及历史版本操作的题,新增代表“时间”的一个维度,刻画在 \(\bm{k + 1}\) 维空间上考虑。
对于此题,发现是查询序列历史版本大于等于 \(v\) 的值的个数,于是可以把它刻画在一个二维平面上,\(x\) 轴代表序列的维度,\(y\) 轴代表时间轴。操作可以转化为:
- 修改操作:将 \(x\in[l, r], y \in [t, q]\) 的矩形加上 \(x\)。
- 查询操作:查询 \(x = p, y\in[0, t]\) 的单列矩形中大于等于 \(v\) 的数的个数。
注意到查询单列矩形的条件很特殊,于是我们可以用一个数据结构维护 \(y\) 轴,然后顺过去扫 \(x\) 轴。
发现即使是一维平面上,带修且查询单个区间中 \(\ge v\) 的个数就只能用分块做了,于是分块维护即可。时间复杂度 \(O(q\sqrt q)\)。
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 100005, M = 1005;
int n, q;
ll a[N], oria[N], ans[N];
struct Operation{ll x, y;
};
vector<Operation> Lines[N], Queries[N];
struct Decompose{int B, Btot, L[M], R[M], bl[N];ll val[N], Tag[M];void rebuild(int p){for(int i = L[p]; i <= R[p]; i++){a[i] = a[i] + Tag[p];val[i] = a[i];}Tag[p] = 0;sort(val + L[p], val + R[p] + 1);}void build(){B = sqrt(q + 1);Btot = (q + 1 + B - 1) / B;for(int i = 1; i <= Btot; i++){L[i] = (i - 1) * B + 1;R[i] = min(q + 1, i * B);for(int j = L[i]; j <= R[i]; j++)bl[j] = i;rebuild(i);}}void update(int l, int r, ll v){if(bl[l] == bl[r]){for(int i = l; i <= r; i++) a[i] += v;rebuild(bl[l]);return;}for(int i = l; i <= R[bl[l]]; i++) a[i] += v;rebuild(bl[l]);for(int i = L[bl[r]]; i <= r; i++) a[i] += v;rebuild(bl[r]);for(int i = bl[l] + 1; i < bl[r]; i++) Tag[i] += v;}int query(int l, int r, ll v){int res = 0;if(bl[l] == bl[r]){for(int i = l; i <= r; i++)res += (a[i] + Tag[bl[l]] >= v);return res;}for(int i = l; i <= R[bl[l]]; i++)res += (a[i] + Tag[bl[l]] >= v);for(int i = L[bl[r]]; i <= r; i++)res += (a[i] + Tag[bl[r]] >= v);for(int i = bl[l] + 1; i < bl[r]; i++){if(val[R[i]] + Tag[i] < v) continue;res += R[i] - (lower_bound(val + L[i], val + R[i] + 1, v - Tag[i]) - val) + 1;}return res;}
}dc;
int main()
{// freopen("P3863.in", "r", stdin);// freopen("P3863.out", "w", stdout);ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n >> q;for(int i = 1; i <= n; i++)cin >> oria[i];dc.build();memset(ans, -1, sizeof(ans));for(int i = 1; i <= q; i++){ll op, x, y, z;cin >> op;if(op == 1){cin >> x >> y >> z;Lines[x].push_back({z, i + 1});Lines[y + 1].push_back({-z, i + 1});}else{cin >> x >> y;Queries[x].push_back({y, i + 1});}}for(int i = 1; i <= n; i++){dc.update(1, q + 1, -oria[i - 1]);dc.update(1, q + 1, oria[i]);for(auto ln : Lines[i])dc.update(ln.y, q + 1, ln.x);for(auto qs : Queries[i])ans[qs.y] = dc.query(1, qs.y - 1, qs.x);}for(int i = 2; i <= q + 1; i++)if(ans[i] != -1)cout << ans[i] << "\n";return 0;
}