寻找宝藏
题意
思路
如果没有矩形陷阱区域的话,设
f
i
f_i
fi 表示从
(
0
,
0
)
(0, 0)
(0,0) 到
(
i
,
p
i
)
(i, p_i)
(i,pi) 的最大收益,那么可以很容易通过扫描线 +
d
p
dp
dp 求出:
f
i
=
v
i
+
max
j
<
i
∧
p
j
<
p
i
f
j
f_i = v_i + \max_{j < i \wedge p_j < p_i} f_j
fi=vi+maxj<i∧pj<pifj,我们只需要按照横坐标从小到大加点,对于当前的
x
=
i
x = i
x=i,我们在树状数组上查询
[
1
,
p
i
−
1
]
[1, p_i - 1]
[1,pi−1] 的最大值即可。这是很典型的处理二维偏序的方法:先固定一维,再在数据结构上查询另外一维
同理,反过来我们也可以得到
g
i
g_i
gi 表示
(
i
,
p
i
)
(i, p_i)
(i,pi) 到
(
n
+
1
,
n
+
1
)
(n + 1, n + 1)
(n+1,n+1) 的最大收益
那么经过某个点 ( i , p i ) (i, p_i) (i,pi) 的最大收益是: h i = f i + g i − v i h_i = f_i + g_i - v_i hi=fi+gi−vi
现在对于一个矩形区域 [ x 1 , x 2 ] × [ y 1 , y 2 ] [x_1, x_2] \times [y_1, y_2] [x1,x2]×[y1,y2],答案来自以下四种情况之一:
- 经过某个位于 [ x 2 + 1 , n ] × [ 1 , y 1 − 1 ] [x_2 + 1, n] \times [1, y_1 - 1] [x2+1,n]×[1,y1−1] 的点 x x x(上图红点),答案为 h x h_x hx
- 经过某个位于 [ 1 , x 1 − 1 ] × [ y 2 + 1 , n ] [1, x_1 - 1] \times [y_2 + 1, n] [1,x1−1]×[y2+1,n] 的点 x x x(上图绿点),答案为 h x h_x hx
- 先经过 [ 1 , x 2 ] × [ 1 , y 1 − 1 ] [1, x_2] \times [1, y_1 - 1] [1,x2]×[1,y1−1] 的某一个点 a a a,再经过 [ x 2 + 1 , n ] × [ y 1 , n ] [x_2 + 1, n] \times [y_1, n] [x2+1,n]×[y1,n] 的某一个点 b b b(先右后上),答案为 f a + g b f_a + g_b fa+gb
- 先经过 [ 1 , x 1 − 1 ] × [ 1 , y 2 ] [1, x_1 - 1] \times [1, y_2] [1,x1−1]×[1,y2] 的某一个点 a a a,再经过 [ x 1 , n ] × [ y 2 + 1 , n ] [x_1, n] \times [y_2 + 1, n] [x1,n]×[y2+1,n] 的某一个点 b b b(先上后右),答案为 f a + g b f_a + g_b fa+gb
可以发现上面四种情况我们都可以通过扫描线处理处理出来,最后答案取 max \max max 即可
归类一下,我们可以先从小到大处理 x x x 坐标,先求出 f f f 的同时,把符合约束的 f x f_x fx 加入到特定的矩形的第 3 , 4 3,4 3,4 种情况的第一个参数中
反过来,我们求出 g g g 的同时,将符合约束的 g x g_x gx 加入到对应矩形的第 3 , 4 3,4 3,4 种情况的第二个参数中
最后有了 f f f 和 g g g 的值之后,我们就可以顺序和倒序再扫描一次,处理 h h h 和第 1 , 2 1,2 1,2 种情况的值了
时间复杂度: ( n + m ) log n (n + m) \log n (n+m)logn
#include<bits/stdc++.h>
#define fore(i,l,r) for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
#define ull unsigned long long
#define ALL(v) v.begin(), v.end()
#define Debug(x, ed) std::cerr << #x << " = " << x << ed;
#define lowbit(x) ((x) & -(x))
const int INF=0x3f3f3f3f;
const long long INFLL=1e18;
typedef long long ll;
const int N = 300005;
struct Point{
int x;
int y;
int v;
};
struct Mat{
Point p1;
Point p2;
int id;
};
ll f[N], g[N];
ll fen[N];
int n, m;
void update(int p, ll val){
while(p <= n){
fen[p] = std::max(fen[p], val);
p += lowbit(p);
}
}
ll query(int p){
ll res = 0;
while(p > 0){
res = std::max(res, fen[p]);
p -= lowbit(p);
}
return res;
}
inline void clear(){
fore(i, 1, n + 1) //clear
fen[i] = 0;
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
int t;
std::cin >> t;
while(t--){
std::cin >> n >> m;
std::vector<Point> a(n);
std::vector<Mat> b(m);
std::vector<std::vector<int>> gl(n + 1), gr(n + 1); //扫描线活动
std::vector<std::array<ll, 4>> ans(m);
fore(i, 0, n){
std::cin >> a[i].y >> a[i].v;
a[i].x = i + 1;
}
fore(i, 0, m){
int x1, y1, x2, y2;
std::cin >> x1 >> y1 >> x2 >> y2;
b[i] = {{x1, y1}, {x2, y2}, i};
gl[x1].push_back(i); //分别在左右边界加入扫描线活动
gr[x2].push_back(i);
}
for(auto [x, y, v] : a){
ll mx = query(y - 1);
f[x] = mx + v;
for(auto id : gl[x]) ans[id][3] = query(b[id].p2.y); //ur
update(y, f[x]); //更新
for(auto id : gr[x]) ans[id][2] = query(b[id].p1.y - 1); //ru
}
clear();
for(int i = n - 1; i >= 0; --i){
auto [x, y, v] = a[i];
ll mx = query(n - y);
g[i + 1] = mx + v;
for(auto id : gr[x]) ans[id][2] += query(n - b[id].p1.y + 1);
update(n - y + 1, g[i + 1]);
for(auto id : gl[x]) ans[id][3] += query(n - b[id].p2.y);
}
clear();
for(int i = n - 1; i >= 0; --i){
auto [x, y, v] = a[i];
for(auto id : gr[x]) ans[id][0] = query(b[id].p1.y - 1);
update(y, f[x] + g[x] - v);
}
clear();
for(auto [x, y, v] : a){
for(auto id : gl[x]) ans[id][1] = query(n - b[id].p2.y);
update(n - y + 1, f[x] + g[x] - v);
}
clear();
for(auto [v1, v2, v3, v4] : ans) std::cout << std::max({v1, v2, v3, v4}) << endl;
}
return 0;
}