【Codeforces Round #433 (Div. 1) C】Boredom(二维线段树)

【链接】我是链接


【题意】



【题解】


这里讲一种用类似二维线段树的方法求矩形区域内点的个数的方法。
我们可以把n个正方形用n棵线段树来维护。
第i棵线段树维护的是正方形的前i列的各行之间的点数之和。
也即前i列,第[x..y]行之间点的个数(也即一个(y-x+1)*i的矩形区域的点的个数);
因为每一列只有一个点。
所以,我们在输入的时候,当前的“行区间l..r”的点数和,直接加上1就好。
然后看看输入的点所在的行区间在l..m还是m+1..r
如果是在l..m,则右区间直接用前一列的线段树的相应节点就好。不用重新建树(因为点数就和前i-1列的一样)。
左区间还是一样,在前一列的基础上点数和递增1.
询问(x1,y1)~(x2,y2)这个矩形区域的时候。
只要在第x1-1列的线段树里面求和第y1..y2行的点个数sum2。
在第x2列的线段树里面也求和第y1..y2行的点个数sum1;
则sum1-sum2就是这个矩形区域内的点的个数了。
其他的和上一篇文章类似。

【错的次数】


0

【反思】


在这了写反思

【代码】

#include <bits/stdc++.h>
using namespace std;

const int N = 2e5, M = 2e5 * 20;

int n, q;
int root[N + 10], ls[M + 10], rs[M + 10], sum[M+10],tot = 0;
long long ans = 0;

void updata(int &x, int y, int l, int r, int pos) {
    x = ++tot;
    ls[x] = ls[y], rs[x] = rs[y];
    sum[x] = sum[y] + 1;    
    int m = (l + r) >> 1;
    if (l == r) return;
    if (pos <= m)
        updata(ls[x], ls[y], l, m, pos);
    else
        updata(rs[x], rs[y], m + 1, r, pos);
}

int Q(int x, int L, int R, int l, int r) {
    if (!x) return 0;
    if (L <= l && r <= R) return sum[x];
    int m = (l + r) >> 1;
    int sum = 0;
    if (L <= m) sum += Q(ls[x], L, R, l, m);
    if (m < R) sum += Q(rs[x], L, R, m + 1, r);
    return sum;
}

long long C(long long x) {
    return x*(x - 1)/2;
}

long long ask(int x1, int y1, int x2, int y2) {
    return C(Q(root[x2], y1, y2, 1, n) - Q(root[x1 - 1], y1, y2, 1, n));
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> n >> q;
    for (int i = 1,p; i <= n; i++) {
        cin >> p;
        updata(root[i],root[i-1],1,n,p);
    }
    for (int i = 1, l, d, r, u; i <= q; i++) {
        cin >> l >> d >> r >> u;
        ans = ask(1, 1, l - 1, n) + ask(1, 1, n, d - 1) + ask(r + 1, 1, n, n) + ask(1, u + 1, n, n);
        ans = ans - ask(1, 1, l - 1, d - 1) - ask(1, u + 1, l - 1, n) - ask(r + 1, u + 1, n, n) - ask(r + 1, 1, n, d - 1);
        cout << C(n) - ans << endl;
    }
    return 0;
}


posted @ 2017-10-04 18:44  AWCXV  阅读(100)  评论(0编辑  收藏  举报