[模拟赛]渔猎(fishing)

前言:

赛后学大神yyf的做法,学了一天才学会,我或鸡蛋。/ll

题目描述:

有一个大小为 \(w \times h\) 且上下联通,左右联通的闭合二维平面,其中有 \(n\) 个矩阵,但是你只知道这 \(n\) 个矩阵的一对角顶点,问在所有可能的方案中,这 \(n\) 个矩阵最大的交是多少?

解题思路:

首先需要发现横纵坐标可以分开做。

然后思考一维怎么做,相当于是一个数轴上,有 \(n\) 个左右端点,每次可以选择 \([l.r]\) 或者 \([1,l-1] \cup [r+1,n]\) 求最大的交。

我们令选择 \([l,r]\) 为选择 \(1\),另一种为选择 \(2\)

那么选择 \(1\) 的所有区间的交就为 \([lmax,rmin]\)\(lmax\) 为所有选 \(1\) 的区间的左端点的最大值,\(rmin\) 同理。

钦定最终的状态为 \([lmax,rmin]\),那么只有 $l \le lmax \wedge r \ge rmin $ 的区间可以选 \(1\),其余只能选 \(2\)

最后 \([lmax,rmin]\) 的最优值就是按照上述方案构造后还能在区间内保留的点数总和。

但是这样时间复杂度太高了,考虑转换计算贡献的方式。

思考一个点 \(pos\) 对于最终区间的答案贡献,只有所有包含这个点的区间都选了 \(1\),才能让这个点留下来,同时我们不用管其他不包含他的区间,因为不包含他的话只需要选 \(2\),就能让他留下来。

那么求出包含点 \(pos\) 的区间的并集 \([L,R]\),所有左端点在 \([L,pos]\),右端点在 \([pos,R]\) 的区间都能加上这个点的贡献。

这可以当作是一个矩阵加,求最值,可以使用二维线段树。当然有更优美的方法,我们将修改放在 \(pos\)\(R+1\) 上,\(pos\) 上放的是区间 \([L,pos]\)\(val\), \(R+1\) 上放的是减 \(val\)。然后询问的时候扫一遍作简单的线段树上区间修改即可。

当然求包含某个点的所有区间的并集,可以使用线段树区间修改,单点求。但是大神教了更加优美的二分加 ST 表。

因为值域有 \(1e9\),所以需要离散化。具体的将区间 \([l,r]\)\(l-1\)\(r\) 丢进离散化数组,这样求某个点的价值时直接将当前的坐标减去离散化数组中前一个坐标即可。这也是大神教的。

代码实现:

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N = 1e6 + 10;
int n, w, h, ans1, ans2, al[N], ar[N];
struct Matrix{int x1, y_1, x2, y2;}a[N];
struct Node{int l, lr, val;};
int tot, tmp[N], vl[N], vr[N];
vector<Node> ins[N];
struct Segment{
    int t[N << 2], tag[N << 2];
    void pushup(int op){
        int ls = op << 1, rs = op << 1 | 1;
        t[op] = max(t[ls], t[rs]);
    }
    void pushdown(int op){
        int ls = op << 1, rs = op << 1 | 1;
        if(tag[op]){
            t[ls] += tag[op], tag[ls] += tag[op];
            t[rs] += tag[op], tag[rs] += tag[op];
            tag[op] = 0;
        }
    }
    void build(int l, int r, int op){
        t[op] = tag[op] = 0;
        if(l == r) return;
        int mid = (l + r) >> 1;
        build(l, mid, op << 1);
        build(mid + 1, r, op << 1 | 1);
    }
    void update(int l, int r, int op, int x, int y, int val){
        if(x <= l && r <= y) return t[op] += val, tag[op] += val, void();
        int mid = (l + r) >> 1;
        pushdown(op);
        if(x <= mid) update(l, mid, op << 1, x, y, val);
        if(y > mid) update(mid + 1, r, op << 1 | 1, x, y, val);
        pushup(op);
    }
    int query(int l, int r, int op){return t[1];}
}T;
int lg[N], st1[N][20], st2[N][20];
int queryl(int l, int r){
    int k = lg[r - l + 1];
    return max(st1[l][k], st1[r - (1 << k) + 1][k]);
}
int queryr(int l, int r){
    int k = lg[r - l + 1];
    return min(st2[l][k], st2[r - (1 << k) + 1][k]);
}
int calcl(int i) {
    int l = 1, r = i, res = 1;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if(queryl(mid, i) >= i) res = mid, l = mid + 1;
        else r = mid - 1;
    }
    return res;
}
int calcr(int i) {
    int l = i, r = tot, res = tot;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if(queryr(i, mid) <= i) res = mid, r = mid - 1;
        else l = mid + 1;
    }
    return res;
}
int solve(int len){
    int res = 0;
    for(int i = 1; i <= n; i++) al[i]++;
    tot = 0, tmp[++tot] = len;
    for(int i = 1; i <= n; i++) tmp[++tot] = al[i] - 1, tmp[++tot] = ar[i];
    sort(tmp + 1, tmp + 1 + tot), tot = unique(tmp + 1, tmp + 1 + tot) - tmp - 1;
    T.build(1, tot, 1);
    for(int i = 1; i <= tot; i++) st1[i][0] = 0, st2[i][0] = tot;
    for(int i = 1; i <= n; i++) al[i] = lower_bound(tmp + 1, tmp + 1 + tot, al[i] - 1) - tmp + 1, ar[i] = lower_bound(tmp + 1, tmp + 1 + tot, ar[i]) - tmp;
    for(int i = 1; i <= n; i++) st1[al[i]][0] = max(st1[al[i]][0], ar[i]), st2[ar[i]][0] = min(st2[ar[i]][0], al[i]);
    for(int j = 1; j <= 19; j++){
        for(int i = 1; i + (1 << j) <= tot + 1; i++){
            st1[i][j] = max(st1[i][j - 1], st1[i + (1 << (j - 1))][j - 1]), st2[i][j] = min(st2[i][j - 1], st2[i + (1 << (j - 1))][j - 1]);
        }
    }
    for(int i = 1; i <= tot; i++){
        int vl = calcl(i), vr = calcr(i);
        ins[i].push_back(Node{vl, i, tmp[i] - tmp[i - 1]}), ins[vr + 1].push_back(Node{vl, i, -tmp[i] + tmp[i - 1]});
    }
    for(int i = 1; i <= tot; i++){
        for(auto [l, r, val] : ins[i]) T.update(1, tot, 1, l, r, val);
        res = max(res, T.query(1, tot, 1)), ins[i].clear();
    }
    ins[tot + 1].clear();
    return res;
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    lg[1] = 0;
    for(int i = 2; i <= N - 10; i++) lg[i] = lg[i / 2] + 1;
    cin >> n >> w >> h;
    for(int i = 1; i <= n; i++) cin >> a[i].x1 >> a[i].y_1 >> a[i].x2 >> a[i].y2;
    for(int i = 1; i <= n; i++) al[i] = min(a[i].x1, a[i].x2), ar[i] = max(a[i].x1, a[i].x2);
    ans1 = solve(w);
    for(int i = 1; i <= n; i++) al[i] = min(a[i].y_1, a[i].y2), ar[i] = max(a[i].y_1, a[i].y2);
    ans2 = solve(h);
    cout << ans1 * ans2 << endl;
    return 0;
}
posted @ 2025-10-31 14:30  _huangweiliang  阅读(9)  评论(0)    收藏  举报