bzoj 1935 [Shoi2007]Tree 园丁的烦恼 cdq分治+树状数组

题面

题目传送门

解法

题目就是一个二维偏序问题

将某一个询问转化成二维前缀和的形式即可

还是用常数比较小的cdq分治吧,空间小,比较容易实现

坐标需要离散化

时间复杂度:\(O(n+m)\ log^2\ n\)

代码

#include <bits/stdc++.h>
#define N 2500010
using namespace std;
template <typename node> void read(node &x) {
    x = 0; int f = 1; char c = getchar();
    while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Que {
    int x1, y1, x2, y2;
} q[N];
struct Node {
    int op, x, y, id, tim;
    bool operator < (const Node &a) const {
        return x < a.x;
    }
} a[N], t[N];
int n, m, sx, sy, x[N], y[N], ans[N], f[N], tx[N], ty[N];
int lowbit(int x) {return x & -x;}
void modify(int x, int v) {
    for (int i = x; i <= sy; i += lowbit(i))
        f[i] += v;
}
int query(int x) {
    int ret = 0;
    for (int i = x; i; i -= lowbit(i))
        ret += f[i];
    return ret;
}
void cdq(int l, int r) {
    if (l == r) return;
    int mid = (l + r) >> 1, tx = l, ty = mid + 1;
    for (int i = l; i <= r; i++)
        if (a[i].tim <= mid) t[tx++] = a[i];
            else t[ty++] = a[i];
    for (int i = l; i <= r; i++) a[i] = t[i];
    tx = l, ty = mid + 1;
    while (ty <= r) {
        while (tx <= mid && a[tx].x <= a[ty].x) {
            if (a[tx].op == 1) modify(a[tx].y, 1);
            tx++;
        }
        if (a[ty].op == 2) ans[a[ty].id] += query(a[ty].y);
        if (a[ty].op == 3) ans[a[ty].id] -= query(a[ty].y);
        ty++;
    }
    for (int i = l; i < tx; i++) if (a[i].op == 1) modify(a[i].y, -1);
    cdq(l, mid), cdq(mid + 1, r);
}
int main() {
    read(n), read(m);
    for (int i = 1; i <= n; i++) {
        read(x[i]), read(y[i]);
        tx[++sx] = x[i], ty[++sy] = y[i];
    }
    for (int i = 1; i <= m; i++) {
        read(q[i].x1), read(q[i].y1), read(q[i].x2), read(q[i].y2);
        tx[++sx] = q[i].x2, tx[++sx] = q[i].x1 - 1;
        ty[++sy] = q[i].y2, ty[++sy] = q[i].y1 - 1;
    }
    sort(tx + 1, tx + sx + 1); sort(ty + 1, ty + sy + 1);
    sx = unique(tx + 1, tx + sx + 1) - tx - 1;
    sy = unique(ty + 1, ty + sy + 1) - ty - 1;
    map <int, int> xx, yy;
    for (int i = 1; i <= sx; i++) xx[tx[i]] = i;
    for (int i = 1; i <= sy; i++) yy[ty[i]] = i;
    int len = 0;
    for (int i = 1; i <= n; i++)
        a[++len] = (Node) {1, xx[x[i]], yy[y[i]], 0, len};
    for (int i = 1; i <= m; i++) {
        int x1 = xx[q[i].x1 - 1], y1 = yy[q[i].y1 - 1];
        int x2 = xx[q[i].x2], y2 = yy[q[i].y2];
        a[++len] = (Node) {2, x2, y2, i, len}, a[++len] = (Node) {3, x1, y2, i, len};
        a[++len] = (Node) {3, x2, y1, i, len}, a[++len] = (Node) {2, x1, y1, i, len};
    }
    sort(a + 1, a + len + 1); cdq(1, len);
    for (int i = 1; i <= m; i++) cout << ans[i] << "\n";
    return 0;
}

转载于:https://www.cnblogs.com/copperoxide/p/9478340.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值