专栏文章

题解:P14426 [JOISC 2014] 稻草人 / Scarecrows

P14426题解参与者 2已保存评论 1

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
1 条
当前快照
1 份
快照标识符
@min9iuoy
此快照首次捕获于
2025/12/01 22:46
3 个月前
此快照最后确认于
2025/12/01 22:46
3 个月前
查看原文
鲜花:为什么想到分治写不出来???是不是根本就不会???
给出平面上 nn 个关键点,求使得左下角和右上角为关键点,且矩形内部不存在其他关键点(不包括边界)的矩形个数。
保证横纵坐标互不相同,n2×105n\le2\times 10^5
固定左下角的点 (x1,y1)(x_1,y_1),考虑什么样的点可以与其配对。假设 (x2,y2)(x_2,y_2) 与其配对。那么一定不存在点 (a,b)(a,b) 使得 a(x1,x2),b(y1,y2)a\in(x_1,x_2),b\in(y_1,y_2)。即不存在在这个范围内被 (x2,y2)(x_2,y_2) 二维偏序的点,转化一下,如果按照横坐标排序,那么 y2y_2 就是横坐标限定在 x>x1x>x_1,纵坐标限制在 y>y1y>y_1 的关于纵坐标的 前缀最小值
直接枚举复杂度甚至不如 O(n2)\mathcal O(n^2) 的朴素暴力,点对问题通常可以用分治解决,考虑分治。由于没有横纵坐标都可以任选,于是随便取一维,这里就取横坐标。考虑左侧点怎么样才可以与右侧点匹配。
设在左侧点为 PP,右侧可以与 PP 匹配的点为 QQ,中线横坐标为 mm。根据前面的限制,QQ 必然是横坐标从 PxP_x 起,所有 y>yPy>y_P 的前缀最小值。然而倘若引入左侧点,难以维护这个前缀最小值。于是考虑转化,这就相当于对于 PxmP_x\sim m 中的最小的比 yPy_P 大的点 UU,使得 yQy_Q 为从 mm 起的前缀最小值且 yQ<yUy_Q<y_U。也就是说,左侧点 PP 要统计纵坐标在 (yP,yU)(y_P,y_U) 之间所有为前缀最小值的右侧点。
考虑对纵坐标扫描线,这样我们就能实时维护对纵坐标有限制的前缀最小值。维护一个单调栈。 由于我们是对纵坐标做的扫描线,因此已经天然满足纵坐标递减的性质。假设栈为 SS,新增点为 (a,b)(a,b),只要当栈顶 StopS_{top} 的横坐标大于 aa,一直弹出。在维护单调栈的同时用树状数组统计值,对于左侧的点的维护也是类似。
复杂度为 O(nlog2n)\mathcal O(n\log^2 n)
CPP
#include <bits/stdc++.h>

using ll = long long;

using namespace std;

const int N = 5e5 + 10;

struct point {
    int x, y;
    int id;
};

struct BIT {
    int n;
    vector <int> t;
    void init (int x) { n = x + 5; t.assign (n + 5, 0); }
    void add (int x, int k) { x ++; for (; x <= n; x += x & -x) t[x] += k; }
    int qry (int x) { x ++; int ret = 0; for (; x; x -= x & -x) ret += t[x]; return ret; }
    int qry (int l, int r) { if (l > r) return 0; return qry (r) - qry (l - 1); }
} bit;

int n;
point p[N];
vector <int> X, Y;
ll ans;

void divide (int l, int r) {
    if (l == r) return ;
    int mid = l + r >> 1;

    sort (p + l, p + mid + 1, [&](auto x, auto y) { return x.y > y.y; });
    sort (p + mid + 1, p + r + 1, [&](auto x, auto y) { return x.y > y.y; });
    vector <int> s, s2;

    auto out = [&]() {
        bit.add (p[s.back ()].y, -1);
        s.pop_back ();
    };
    auto in = [&](int x) {
        bit.add (p[x].y, 1);
        s.push_back (x);
    };

    int j = mid + 1;
    for (int i = l; i <= mid; i ++) {
        while (j <= r && p[j].y > p[i].y) {
            while (!s.empty () && p[s.back ()].x > p[j].x) out ();
            in (j ++);
        }
        while (!s2.empty () && p[s2.back ()].x < p[i].x) s2.pop_back ();
        int up = s2.empty () ? n : p[s2.back ()].y;
        ans += bit.qry (p[i].y, up);
        s2.push_back (i);
    }

    while (s.size ()) out ();
    
    sort (p + l, p + r + 1, [&](auto x, auto y) { return x.x < y.x; });
    divide (l, mid);
    divide (mid + 1, r);
} 

int main (void) {

    scanf ("%d", &n); bit.init (n);
    for (int i = 1; i <= n; i ++) scanf ("%d%d", &p[i].x, &p[ p[i].id = i ].y), X.push_back (p[i].x), Y.push_back (p[i].y);
    sort (X.begin (), X.end ()); sort (Y.begin (), Y.end ());
    for (int i = 1; i <= n; i ++) 
        p[i].x = lower_bound (X.begin (), X.end (), p[i].x) - X.begin () + 1,
        p[i].y = lower_bound (Y.begin (), Y.end (), p[i].y) - Y.begin () + 1;
    sort (p + 1, p + n + 1, [&](auto x, auto y) { return x.x < y.x; });

    divide (1, n);

    printf ("%lld\n", ans);

    return 0;
}

评论

1 条评论,欢迎与作者交流。

正在加载评论...