【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; }