二维数点 & 扫描线
对扫描线不熟悉到不怎么会打了QAQ。
二维数点
这是一个很简单的板块,今天来讲讲进阶的玩法。
他的本质其实是通过树状数组或其他数据结构对于形如 \(l_i \le x \le l_j,r_i \le y \le r_j\)的式子进行加速!
例题:P10281 [USACO24OPEN] Grass Segments G
这里给出两个解法
- cdq 分治
这个方法有点无脑。
对于 \((l_i, r_i, k_i)\) ,有 \((l_j, r_j)\) 满足条件当且仅当:
\(k_i \le \min(r_i,r_j) - \max(l_i,l_j)\)
那么把他拆开就是:
那么显然第一个式子完全是成立的。
所以就剩下了:
在整理一下变为:
我们发现条件好像无法直接维护,考虑新建 \(n\) 个虚点,其 k 的值为 \(r_j−l_j\),此时按照 \(k\) 的值从小到大进行排序即可。
此时我们要求的是右区间对左区间的贡献,考虑左区间按照 \(l_i+k\) 从大到小排序,右区间按照 \(r_j\) 从大到小排序,每次走指针时将满足条件的虚点的 \(l_j\) 加入树状数组,最后对于每一个实点,求出 \([1,r_i−k]\) 内的点的数量即可。
时间复杂度为:\(O(N \log^2N)\)
2.二维数点方法
\(\min(r_i,r_j) \ge k_i+\max(l_i,l_j)\)
分类讨论,如果 \(r_j \ge r_i\),那么\(\min(r_i,r_j)=r_i\)
那么 \(r_i \ge k_i+\max(l_i,l_j)\),因为已经有 \(r_i \ge k_i+l_i\) 恒成立,所以得到第一种情况:
\(r_j \ge r_i \ge l_j+k_i\),整理后得到 \(r_j \ge r_i,l_j \le r_i-k_i\)
如果 \(r_j \lt r_i\),那么 \(r_j \ge k_i+\max(l_i,l_j)\),此时 \(r_j \ge k_i+l_i\) 且 \(r_j \ge k_i+l_j\)。
整理后得到第二种情况:\(r_j \ge l_i+k_i,r_j \lt r_i,r_j - l_j \ge k_i\)
情况一:\(r_j \ge r_i,l_j \le r_i-k_i\)
情况二:\(r_j \ge l_i+k_i,r_j \lt r_i,r_j - l_j \ge k_i\)
显然这两种情况不能同时满足,我们发现只用分别维护 \(2\) 个二维偏序即可。
时间复杂度: \(O(N \log N)\)
扫描线
其实这类题大部分只是扫描线的思想但不用写。
其实本质上也就是差分/扫描线。
先来一个简单的例题吧
P9478 [NOI2023] 方格染色
注意到这道题其实斜线最多只有五条且斜率一样,但是斜线也有可能有重叠,所以要先处理。
然后我们发现横线与竖线我们可以直接按照扫描线去扫,而斜线就直接加就行了。
可是斜线可能与横线或竖线重叠,所以要记录所有重复的点并去重最后减掉就行。
戳我喵~
```
int n, m, q;
int x, x2, y, y2;
struct Tree {
int l, r, c;
int sum, fl, fr;
} tree[N << 2];
struct node {
int x, y, y2;
int f;
} line[N << 1];
struct slush {
int x, y, xx, yy;
}edge[N];
int s[N];
bool cmp(node a, node b) {
return a.x < b.x;
}
void build(int l, int r, int rt) {
tree[rt].l = l, tree[rt].r = r, tree[rt].c = tree[rt].sum = 0;
tree[rt].fl = s[l], tree[rt].fr = s[r];
if (l + 1 == r) return;
int mid = l + r >> 1;
build(l, mid, rt << 1);
build(mid, r, rt << 1 | 1);
}
void calc(int rt) {
if (tree[rt].c > 0) {
tree[rt].sum = tree[rt].fr - tree[rt].fl;
return;
}
if (tree[rt].l + 1 == tree[rt].r) tree[rt].sum = 0;
else tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum;
}
void update(int rt, int ql, int qr, int val) {
if (ql <= tree[rt].l && tree[rt].r <= qr) {
tree[rt].c += val;
calc(rt);
return;
}
int mid = (tree[rt].l + tree[rt].r) >> 1;
if (ql < mid) update(rt << 1, ql, qr, val);
if (qr > mid) update(rt << 1 | 1, ql, qr, val);
calc(rt);
}
signed main() {
int HYWHYWHYWHYWHYWHYWHYW = re;
n = re, m = re, q = re;
int tot = 0, cnt = 0;
for (int i = 1; i <= q; i++) {
int op = re;
if (op == 3) edge[++cnt] = {re, re, re, re};
else {
x = re, y = re, x2 = re + 1, y2 = re + 1;
line[++tot].x = x, line[tot].y = y, line[tot].y2 = y2;
line[tot].f = 1;
s[tot] = y;
line[++tot].x = x2, line[tot].y = y, line[tot].y2 = y2;
line[tot].f = -1;
s[tot] = y2;
}
}
bool flag = 0;
while (!flag) {
flag = 1;
for (int i = 1; i <= cnt; i++) for (int j = 1; j <= cnt; j++) {
if (i == j) continue;
if (edge[i].x - edge[i].y == edge[j].x - edge[j].y && max(edge[i].y, edge[j].y) <= min(edge[i].yy, edge[j].yy)) {
edge[i] = {min(edge[i].x, edge[j].x), min(edge[i].y, edge[j].y), max(edge[i].xx, edge[j].xx), max(edge[i].yy, edge[j].yy)}, flag = 0;
edge[j] = {0, 0, 0, 0};
swap(edge[j], edge[cnt]), cnt--;
}
}
}
set<pii> point;
int ans = 0;
for (int i = 1; i <= cnt; i++) ans += edge[i].yy - edge[i].y + 1;
for (int i = 1; i <= cnt; i++) for (int j = 1; j <= tot; j += 2) {
if (line[j].x + 1 == line[j + 1].x) {
int diff = line[j].x - edge[i].x, new_y = edge[i].y + diff;
if (diff >= 0 && new_y >= line[j].y && new_y <= line[j].y2 - 1 && new_y <= edge[i].yy) point.insert({line[j].x, new_y});
}
else {
int diff = line[j].y - edge[i].y, new_x = edge[i].x + diff;
if (diff >= 0 && new_x >= line[j].x && new_x <= line[j + 1].x - 1 && new_x <= edge[i].xx) point.insert({new_x, line[j].y});
}
}
ans -= point.size();
sort(s + 1, s + 1 + tot);
int KKK = unique(s + 1, s + 1 + tot) - (s + 1);
sort(line + 1, line + 1 + tot, cmp);
build(1, KKK, 1);
for (int i = 1; i <= tot; i++) line[i].y = lower_bound(s + 1, s + 1 + KKK, line[i].y) - s, line[i].y2 = lower_bound(s + 1, s + 1 + KKK, line[i].y2) - s;
update(1, line[1].y, line[1].y2, line[1].f);
for (int i = 2; i <= tot; i++) {
ans += tree[1].sum * (line[i].x - line[i - 1].x);
update(1, line[i].y, line[i].y2, line[i].f);
}
wr(ans), endl;
}
```
</details>

浙公网安备 33010602011771号