2024杭电多校(4)1012. 寻找宝藏 【扫描线、树状数组二维偏序】

寻找宝藏

题意

1

2

思路

如果没有矩形陷阱区域的话,设 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<ipj<pifj,我们只需要按照横坐标从小到大加点,对于当前的 x = i x = i x=i,我们在树状数组上查询 [ 1 , p i − 1 ] [1, p_i - 1] [1,pi1] 的最大值即可。这是很典型的处理二维偏序的方法:先固定一维,再在数据结构上查询另外一维
同理,反过来我们也可以得到 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+givi

现在对于一个矩形区域 [ x 1 , x 2 ] × [ y 1 , y 2 ] [x_1, x_2] \times [y_1, y_2] [x1,x2]×[y1,y2],答案来自以下四种情况之一:

3

  1. 经过某个位于 [ x 2 + 1 , n ] × [ 1 , y 1 − 1 ] [x_2 + 1, n] \times [1, y_1 - 1] [x2+1,n]×[1,y11] 的点 x x x(上图红点),答案为 h x h_x hx
  2. 经过某个位于 [ 1 , x 1 − 1 ] × [ y 2 + 1 , n ] [1, x_1 - 1] \times [y_2 + 1, n] [1,x11]×[y2+1,n] 的点 x x x(上图绿点),答案为 h x h_x hx
  3. 先经过 [ 1 , x 2 ] × [ 1 , y 1 − 1 ] [1, x_2] \times [1, y_1 - 1] [1,x2]×[1,y11] 的某一个点 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
  4. 先经过 [ 1 , x 1 − 1 ] × [ 1 , y 2 ] [1, x_1 - 1] \times [1, y_2] [1,x11]×[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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值