P5816 [CQOI2010]内部白点
扫描线的做法这里不过多赘述了,挺多题解都讲的挺不辍的。这里分享一个主席树的写法。
首先我们明确的是,第一步变化之后,整个图就不会变了。为什么呢,第二步才改变的点 q q q 肯定是因为第一步改变的某个点 p p p 或者好几个点变成黑点才改变的。如果 q q q 因为 p p p 而改变且 p p p 在 q q q 的水平左侧,那么 p p p 左侧肯定还有点,因为 p p p 是第一步变成黑点的点,那么在此情况下, q q q 第一步也应当变成黑点,不应该等到第二次才变化。其他几个方向或者多个点的情况是同理的。也就是说第二步整个图就不会发生变化了,因此以后永远也不会再变化。
那么会从白变黑的点肯定是夹在两个 y y y 相等的相邻点中间的点,现将这两个相邻点表示为 ( x 1 , y ) (x_1, y) (x1,y) 和 ( x 2 , y ) (x_2, y) (x2,y) ,那么我们只要对每一对这样的相邻点去求答案最后加和就可以了。那么其中间的点可以表示为 { ( x , y ) ∣ x ∈ [ x 1 + 1 , x 2 − 1 ] } \{(x,y)~~|~x∈[x_1+1,x_2-1]\} {(x,y) ∣ x∈[x1+1,x2−1]} ,那么这里有多少个点可以变成黑点其实也就是对于这里的每个 x x x ,去看是否其上下分别至少都存在一个点,但是我们发现这样不好计算,于是我们反过来算,我们算有多少个不满足条件。不满足条件就是,对于 x x x 这一列来说,要么只有 y y y 的上方没有点,要么只有下侧没有点,要么都没。那么我就想:
①以纵轴为时间轴建立主席树,可以算出 y y y 上方在 [ x 1 + 1 , x 2 − 1 ] [x_1+1,x_2-1] [x1+1,x2−1] 内有多少种 x x x ,那么用总的种类数减一下,也就可以算出缺了多少种 x x x ,也就算出了 y y y 的上方有多少纵列是空的。
②再以纵轴为时间轴反向建立主席树,就可以得到 y y y 的下方在 [ x 1 + 1 , x 2 − 1 ] [x_1+1,x_2-1] [x1+1,x2−1] 内有多少种 x x x,同理也可计算出 y y y 下方有多少纵列是空的。
③然后我们用正向建立还是反向建立的主席树都可得到对于全局在 [ x 1 + 1 , x 2 − 1 ] [x_1+1,x_2-1] [x1+1,x2−1] 内有多少种 x x x ,同理算出全局 [ x 1 + 1 , x 2 − 1 ] [x_1+1,x_2-1] [x1+1,x2−1] 内有多少纵列是空的。
这里我们将前两项相加减去第三项即可算出 [ x 1 + 1 , x 2 − 1 ] [x_1+1,x_2-1] [x1+1,x2−1] 内,有多少纵列要么 y y y 上面是空的,要么 y y y 下面是空的,要么 y y y 的上下全是空的。说白了我们就算出了无法变成黑点的情况,那么减一下,不就变成了可以变成黑点的情况。
注意这里用主席树没用使用区间求值的操作,因为种类数不满足前缀相减的性质,所以这里正反建了两颗主席树。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n;
int tree[2][N << 5], lson[2][N << 5], rson[2][N << 5], root[2][N], tot[2];
int ytot;
pair<int, int> p[N];
vector<int> g[N];
void update(int p, int pre, int &rt, int l, int r, int x) {
rt = ++tot[p];
tree[p][rt] = tree[p][pre];
lson[p][rt] = lson[p][pre];
rson[p][rt] = rson[p][pre];
if (l == r) {
if (!tree[p][rt]) tree[p][rt] = 1;
return ;
}
int mid = l + r >> 1;
if (x <= mid) update(p, lson[p][pre], lson[p][rt], l, mid, x);
else update(p, rson[p][pre], rson[p][rt], mid + 1, r, x);
tree[p][rt] = tree[p][lson[p][rt]] + tree[p][rson[p][rt]];
}
int query(int p, int rt, int l, int r, int L, int R) {
if (L <= l && r <= R) {
return tree[p][rt];
}
int mid = l + r >> 1, sum = 0;
if (mid >= L) sum = query(p, lson[p][rt], l, mid, L, R);
if (mid < R) sum += query(p, rson[p][rt], mid + 1, r, L, R);
return sum;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
scanf("%d", &n);
ll ans = n;
for (int i = 1; i <= n; ++i) {
scanf("%d%d", &p[i].second, &p[i].first);
}
sort(p + 1, p + 1 + n);
p[0].first = 2e9, p[0].second = 2e9;
for (int i = 1; i <= n; ++i) {
if (p[i].first == p[i - 1].first) {
update(0, root[0][ytot], root[0][ytot], -1e9, 1e9, p[i].second);
g[ytot].push_back(p[i].second);
}
else {
update(0, root[0][ytot], root[0][ytot + 1], -1e9, 1e9, p[i].second);
g[++ytot].push_back(p[i].second);
}
}
p[n + 1].first = 2e9, p[n + 1].second = 2e9, ytot = 0;
for (int i = n; i >= 1; --i) {
if (p[i].first == p[i + 1].first) {
update(1, root[1][ytot], root[1][ytot], -1e9, 1e9, p[i].second);
}
else {
update(1, root[1][ytot], root[1][ytot + 1], -1e9, 1e9, p[i].second);
ytot++;
}
}
for (int i = 1; i <= ytot; ++i) {
sort(g[i].begin(), g[i].end());
for (int j = 1; j < (int)g[i].size(); ++j) {
int L = g[i][j - 1] + 1, R = g[i][j] - 1;
if (L > R) continue;
int len = (R - L + 1);
int up = len - query(0, root[0][i], -1e9, 1e9, L, R);
int down = len - query(1, root[1][ytot - i + 1], -1e9, 1e9, L, R);
int all = len - query(0, root[0][ytot], -1e9, 1e9, L, R);
int white = up + down - all;
int black = len - white;
ans += black;
}
}
printf("%lld", ans);
}