【链接】我是链接
【题意】
【题解】
这里讲一种用类似二维线段树的方法求矩形区域内点的个数的方法。
我们可以把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;
}