# 二维数点
顾名思义,就是在一个二维平面内数一个矩形内有多少个点。
例题:P2163 [SHOI2007] 园丁的烦恼
# 树状数组
根据二维前缀和的算法,把矩形和改成求 4 个坐标 到原点的矩形和,再容斥计算答案。
首先对坐标离散化,按 轴排序,从左到右依次插入每个点。对 轴维护一个树状数组。
插入:在相应的 轴坐标上加 1.
查询:由于按 轴从小到大插入的,树状数组内的点都在当前的 左边。所以查询小于当前 的和就是 到 的和。
//Lugou P2163 | |
#include <bits/stdc++.h> | |
#define pb push_back | |
using namespace std;  | |
namespace IO{  | |
inline int read(){  | |
int x = 0, f = 1;  | |
char ch = getchar();  | |
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}  | |
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();  | |
return x * f;  | |
    } | |
template <typename T> inline void write(T x){  | |
if(x < 0) putchar('-'), x = -x;  | |
if(x > 9) write(x / 10);  | |
putchar(x % 10 + '0');  | |
    } | |
} | |
using namespace IO;  | |
const int N = 2.5e6 + 10;  | |
int n, m;  | |
struct node{  | |
int x, y, id, typ;  | |
inline bool operator < (const node &b) const{  | |
return x != b.x ? x < b.x : (y != b.y ? y < b.y : id < b.id);  | |
    } | |
}p[N];  | |
int b[N], ans[N];  | |
int c[N];  | |
inline void add(int x, int y){  | |
for(; x <= n; x += x & (-x)) c[x] += y;  | |
} | |
inline int qry(int x){  | |
int res = 0;  | |
for(; x; x -= x & (-x)) res += c[x];  | |
return res;  | |
} | |
signed main(){  | |
#ifndef ONLINE_JUDGE | |
freopen("test.in", "r", stdin);  | |
freopen("test.out", "w", stdout);  | |
#endif | |
n = read(), m = read();  | |
for(int i = 1; i <= n; ++i)  | |
p[i] = (node){read(), read(), 0, 0};  | |
for(int i = 1; i <= m; ++i){  | |
int x1 = read(), y1 = read(), x2 = read(), y2 = read();  | |
p[++n] = (node){x1 - 1, y1 - 1, i, 1};  | |
p[++n] = (node){x1 - 1, y2, i, -1};  | |
p[++n] = (node){x2, y1 - 1, i, -1};  | |
p[++n] = (node){x2, y2, i, 1};  | |
    } | |
sort(p + 1, p + 1 + n);  | |
for(int i = 1; i <= n; ++i) b[i] = p[i].y;  | |
sort(b + 1, b + 1 + n);  | |
int tot = unique(b + 1, b + 1 + n) - b - 1;  | |
for(int i = 1; i <= n; ++i){  | |
int pos = lower_bound(b + 1, b + 1 + tot, p[i].y) - b;  | |
if(p[i].id) ans[p[i].id] += p[i].typ > 0 ? qry(pos) : -qry(pos);  | |
else add(pos, 1);  | |
    } | |
for(int i = 1; i <= m; ++i) write(ans[i]), puts("");  | |
return 0;  | |
} | 
# 线段树
很显然扫描线也是可以处理这个问题的。
还是先把所有的点离散化。
对 轴维护线段树,扫描线横着扫。把一个询问 拆成两个部分,减去 再加上 。
和 的贡献分两次计算。
先按 从小到大排序,对于询问 把 的点插到线段树里,然后查询答案。
计算 的贡献时再按 从小到大排序,其他同理。
#include <bits/stdc++.h> | |
#define pb push_back | |
#define find(x) lower_bound(b + 1, b + 1 + tot, x) - b | |
#define ls rt << 1 | |
#define rs rt << 1 | 1 | |
#define mid ((l + r) >> 1) | |
using namespace std;  | |
namespace IO{  | |
inline int read(){  | |
int x = 0, f = 1;  | |
char ch = getchar();  | |
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}  | |
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();  | |
return x * f;  | |
    } | |
template <typename T> inline void write(T x){  | |
if(x < 0) putchar('-'), x = -x;  | |
if(x > 9) write(x / 10);  | |
putchar(x % 10 + '0');  | |
    } | |
} | |
using namespace IO;  | |
const int N = 5e5 + 10;  | |
int n, m;  | |
struct node{  | |
int x, y;  | |
inline bool operator < (const node &b) const{  | |
return x != b.x ? x < b.x : y < b.y;  | |
    } | |
}p[N];  | |
struct Query{  | |
int x1, y1, x2, y2, id;  | |
inline bool operator < (const Query &b) const{  | |
return x1 < b.x1;  | |
    } | |
}q[N];  | |
int b[N << 3], tot;  | |
inline bool cmp(Query a, Query b) {return a.x2 < b.x2;}  | |
int sum[N << 3], ans[N];  | |
inline void pushup(int rt){  | |
sum[rt] = sum[ls] + sum[rs];  | |
} | |
inline void update(int x, int l, int r, int rt){  | |
if(l == r) return sum[rt]++, void();  | |
if(x <= mid) update(x, l, mid, ls);  | |
else update(x, mid + 1, r, rs);  | |
pushup(rt);  | |
} | |
inline int query(int L, int R, int l, int r, int rt){  | |
if(L > r || R < l) return 0;  | |
if(L <= l && r <= R) return sum[rt];  | |
return query(L, R, l, mid, ls) + query(L ,R, mid + 1, r, rs);  | |
} | |
signed main(){  | |
#ifndef ONLINE_JUDGE | |
freopen("test.in", "r", stdin);  | |
freopen("test.out", "w", stdout);  | |
#endif | |
n = read(), m = read();  | |
for(int i = 1; i <= n; ++i)  | |
p[i] = (node){read() + 1, read() + 1}, b[++tot] = p[i].x, b[++tot] = p[i].y;  | |
for(int i = 1; i <= m; ++i){  | |
q[i] = (Query){read() + 1, read() + 1, read() + 1, read() + 1, i};  | |
b[++tot] = q[i].x1, b[++tot] = q[i].y1, b[++tot] = q[i].x2, b[++tot] = q[i].y2;  | |
    } | |
sort(b + 1, b + 1 + tot);  | |
tot = unique(b + 1, b + 1 + tot) - b - 1;  | |
for(int i = 1; i <= n; ++i)  | |
p[i].x = find(p[i].x), p[i].y = find(p[i].y);  | |
for(int i = 1; i <= m; ++i){  | |
q[i].x1 = find(q[i].x1), q[i].y1 = find(q[i].y1);  | |
q[i].x2 = find(q[i].x2), q[i].y2 = find(q[i].y2);  | |
    } | |
sort(p + 1, p + 1 + n);  | |
sort(q + 1, q + 1 + m);  | |
int pos = 0;  | |
for(int i = 1; i <= m; ++i){  | |
while(pos < n && q[i].x1 > p[pos + 1].x) update(p[++pos].y, 1, n, 1);  | |
ans[q[i].id] -= query(q[i].y1, q[i].y2, 1, n, 1);  | |
    } | |
memset(sum, 0, sizeof(sum)), pos = 0;  | |
sort(q + 1, q + 1 + m, cmp);  | |
for(int i = 1; i <= m; ++i){  | |
while(pos < n && q[i].x2 >= p[pos + 1].x) update(p[++pos].y, 1, n, 1);  | |
ans[q[i].id] += query(q[i].y1, q[i].y2, 1, n, 1);  | |
    } | |
for(int i = 1; i <= m; ++i) write(ans[i]), puts("");  | |
return 0;  | |
} | 
# 主席树
对 轴维护主席树,按 轴从小到大依次插入点。
查询时,查询权值在 的 即可。
个人认为主席树这样的想法类似于扫描线,而且实现比线段树实现优美的多。
#include <bits/stdc++.h> | |
#define pb push_back | |
#define mid ((l + r) >> 1) | |
using namespace std;  | |
namespace IO{  | |
inline int read(){  | |
int x = 0, f = 1;  | |
char ch = getchar();  | |
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}  | |
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();  | |
return x * f;  | |
    } | |
template <typename T> inline void write(T x){  | |
if(x < 0) putchar('-'), x = -x;  | |
if(x > 9) write(x / 10);  | |
putchar(x % 10 + '0');  | |
    } | |
} | |
using namespace IO;  | |
const int N = 5e5 + 10;  | |
const int M = 1e7;  | |
int n, m;  | |
struct node{  | |
int x, y;  | |
inline bool operator < (const node &b) const{  | |
return x < b.x;  | |
    } | |
}a[N];  | |
int b[N], tot;  | |
int rt[N << 5], ls[N << 5], rs[N << 5], sum[N << 5];  | |
int cnt;  | |
inline int update(int pre, int l, int r, int x){  | |
int p = ++cnt;  | |
ls[p] = ls[pre], rs[p] = rs[pre], sum[p] = sum[pre] + 1;  | |
if(l == r) return p;  | |
if(x <= mid) ls[p] = update(ls[pre], l, mid, x);  | |
else rs[p] = update(rs[pre], mid + 1, r, x);  | |
return p;  | |
} | |
inline int query(int p, int l, int r, int L, int R){  | |
if(!p || L > r || R < l) return 0;  | |
if(L <= l && r <= R) return sum[p];  | |
return query(ls[p], l, mid, L, R) + query(rs[p], mid + 1, r, L, R);  | |
} | |
signed main(){  | |
#ifndef ONLINE_JUDGE | |
freopen("test.in", "r", stdin);  | |
freopen("test.out", "w", stdout);  | |
#endif | |
n = read(), m = read();  | |
for(int i = 1; i <= n; ++i) a[i] = (node){read(), read()}, b[i] = a[i].x;  | |
sort(a + 1, a + 1 + n), sort(b + 1, b + 1 + n), b[n + 1] = M;  | |
for(int i = 1; i <= n; ++i) rt[i] = update(rt[i - 1], 0, M, a[i].y);  | |
for(int i = 1; i <= m; ++i){  | |
int x1 = read(), y1 = read(), x2 = read(), y2 = read();  | |
x1 = lower_bound(b + 1, b + 1 + n, x1) - b;  | |
x2 = upper_bound(b + 1, b + 1 + n, x2) - b - 1;  | |
write(query(rt[x2], 0, M, y1, y2) - query(rt[x1 - 1], 0, M, y1, y2)), puts("");  | |
    } | |
return 0;  | |
} |