P6109 [Ynoi2009] rprmq1 题解

Description

有一个 n×nn \times n 的矩阵 aa,初始全是 00,有 mm 次修改操作和 qq 次查询操作,先进行所有修改操作,然后进行所有查询操作。

一次修改操作会给出 l1,l2,r1,r2,xl_1,l_2,r_1,r_2,x,代表把所有满足 l1ir1l_1 \le i \le r_1l2jr2l_2 \le j \le r_2ai,ja_{i,j} 元素加上一个值 xx

一次查询操作会给出 l1,l2,r1,r2l_1,l_2,r_1,r_2,代表查询所有满足 l1ir1l_1 \le i \le r_1l2jr2l_2 \le j \le r_2ai,ja_{i,j} 元素的最大值。

1n,m5×1041\leq n,m\leq 5\times 10^41q5×1051\leq q \leq 5\times 10^51x21474836471\leq x\leq 21474836471l1r1n1\leq l_1\leq r_1\leq n1l2r2n1\leq l_2\leq r_2\leq n

Solution

容易发现可以扫描线,但是直接做的话查询时需要维护任意一段时刻内的区间最大值,这显然是做不了的。

但是如果所有的查询 l1l_1 均相等的话就可以从小到大枚举修改操作和查询的 r1r_1,这样只需要通过区间加、历史最大值线段树维护当前所有操作的区间历史最大值。

这可以启发我们进行类似猫树分治的做法,从浅到深枚举线段树的每一层,找到所有横跨当前层不同节点的询问放在当前层做,剩下的放到更深层做。

由于横跨当前层不同节点的询问 [l1,r1][l_1,r_1] 一定只跨过两个节点,所以这两个节点可以把它切割成 [l1,k][l_1,k][k+1,r1][k+1,r_1],对于 [k+1,r1][k+1,r_1] 这部分只需要维护一个历史最大值线段树,然后从小到大做修改操作,在每个线段树节点的开头重置历史最大值为当前最大值即可。[l1,k][l_1,k] 的部分同理。

时间复杂度:O(nlog2n+qlogn)O(n\log^2n+q\log n)

Code

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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include <bits/stdc++.h>

#define int int64_t

const int kMaxN = 5e4 + 5, kMaxQ = 5e5 + 5;

int n, m, q;
int l1[kMaxQ], r1[kMaxQ], l2[kMaxQ], r2[kMaxQ], ans[kMaxQ];
bool vis[kMaxQ];
std::vector<std::tuple<int, int, int, int, int>> ud;
std::vector<std::tuple<int, int, int>> qq[kMaxN], vec[kMaxN];

struct SGT {
int maxa[kMaxN * 8], maxb[kMaxN * 8], tag1[kMaxN * 8], tag2[kMaxN * 8], tagr[kMaxN * 8];

void pushup(int x) {
maxa[x] = std::max(maxa[x << 1], maxa[x << 1 | 1]);
maxb[x] = std::max(maxb[x << 1], maxb[x << 1 | 1]);
}

void addtag(int x, int v1, int v2) {
maxb[x] = std::max(maxb[x], maxa[x] + v2);
tag2[x] = std::max(tag2[x], tag1[x] + v2);
maxa[x] += v1, tag1[x] += v1;
}

void reset(int x) {
addtag(x << 1, tag1[x], tag2[x]), addtag(x << 1 | 1, tag1[x], tag2[x]);
maxb[x] = maxa[x], tagr[x] = 1;
tag1[x] = tag2[x] = 0;
}

void pushdown(int x) {
if (tagr[x]) {
reset(x << 1), reset(x << 1 | 1);
tagr[x] = 0;
}
if (tag1[x] || tag2[x]) {
addtag(x << 1, tag1[x], tag2[x]), addtag(x << 1 | 1, tag1[x], tag2[x]);
tag1[x] = tag2[x] = 0;
}
}

void build(int x, int l, int r) {
maxa[x] = maxb[x] = tag1[x] = tag2[x] = tagr[x] = 0;
if (l == r) return;
int mid = (l + r) >> 1;
build(x << 1, l, mid), build(x << 1 | 1, mid + 1, r);
}

void update(int x, int l, int r, int ql, int qr, int v) {
if (l > qr || r < ql) return;
else if (l >= ql && r <= qr) return addtag(x, v, std::max<int>(v, 0));
pushdown(x);
int mid = (l + r) >> 1;
update(x << 1, l, mid, ql, qr, v), update(x << 1 | 1, mid + 1, r, ql, qr, v);
pushup(x);
}

int query(int x, int l, int r, int ql, int qr) {
if (l > qr || r < ql) return 0;
else if (l >= ql && r <= qr) return maxb[x];
pushdown(x);
int mid = (l + r) >> 1;
return std::max(query(x << 1, l, mid, ql, qr), query(x << 1 | 1, mid + 1, r, ql, qr));
}
} sgt;

void solve(int d) {
for (int i = 1; i <= n; ++i) vec[i].clear(), qq[i].clear();
for (auto [l1, r1, l2, r2, x] : ud)
vec[l1].emplace_back(l2, r2, x), vec[r1 + 1].emplace_back(l2, r2, -x);
for (int i = 1; i <= q; ++i) {
if (!vis[i] && ((l1[i] >> d) != (r1[i] >> d) || !d)) {
if (l1[i] != r1[i]) assert((l1[i] >> d) == (r1[i] >> d) - 1);
qq[r1[i]].emplace_back(i, l2[i], r2[i]);
}
}
sgt.build(1, 1, n);
for (int i = 1; i <= n; ++i) {
for (auto [l, r, v] : vec[i]) {
if (v < 0) sgt.update(1, 1, n, l, r, v);
}
for (auto [l, r, v] : vec[i]) {
if (v >= 0) sgt.update(1, 1, n, l, r, v);
}
if (i % (1 << d) == 0) sgt.reset(1);
for (auto [id, l, r] : qq[i]) ans[id] = std::max(ans[id], sgt.query(1, 1, n, l, r));
}

for (int i = 1; i <= n; ++i) vec[i].clear(), qq[i].clear();
for (auto [l1, r1, l2, r2, x] : ud)
vec[r1].emplace_back(l2, r2, x), vec[l1 - 1].emplace_back(l2, r2, -x);
for (int i = 1; i <= q; ++i) {
if (!vis[i] && ((l1[i] >> d) != (r1[i] >> d) || !d)) {
if (l1[i] != r1[i]) assert((l1[i] >> d) == (r1[i] >> d) - 1);
vis[i] = 1;
qq[l1[i]].emplace_back(i, l2[i], r2[i]);
}
}
sgt.build(1, 1, n);
for (int i = n; i; --i) {
for (auto [l, r, v] : vec[i]) {
if (v < 0) sgt.update(1, 1, n, l, r, v);
}
for (auto [l, r, v] : vec[i]) {
if (v >= 0) sgt.update(1, 1, n, l, r, v);
}
if (i % (1 << d) == (1 << d) - 1) sgt.reset(1);
for (auto [id, l, r] : qq[i]) ans[id] = std::max(ans[id], sgt.query(1, 1, n, l, r));
}
}

void dickdreamer() {
std::cin >> n >> m >> q;
for (int i = 1; i <= m; ++i) {
int l1, l2, r1, r2, x;
std::cin >> l1 >> l2 >> r1 >> r2 >> x;
ud.emplace_back(l1, r1, l2, r2, x);
}
for (int i = 1; i <= q; ++i)
std::cin >> l1[i] >> l2[i] >> r1[i] >> r2[i];
for (int i = std::__lg(n) + 1; ~i; --i) {
solve(i);
}
for (int i = 1; i <= q; ++i) std::cout << ans[i] << '\n';
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

P6109 [Ynoi2009] rprmq1 题解
https://sobaliuziao.github.io/2024/08/16/post/b276de57.html
作者
Egg_laying_master
发布于
2024年8月16日
许可协议