10.26 Bzoj1818 [Cqoi2010]内部白点(树状数组+扫描线)

题意

无限大正方形网格里有n个黑色的顶点,所有其他顶点都是白色的(网格的顶点即坐标为整数的点,又称整点)。每秒钟,所有内部白点同时变黑,直到不存在内部白点为止。你的任务是统计最后网格中的黑点个数。 内部白点的定义:一个白色的整点P(x,y)是内部白点当且仅当P在水平线的左边和右边各至少有一个黑点(即存在x1 < x < x2使得(x1,y)和(x2,y)都是黑点),且在竖直线的上边和下边各至少有一个黑点(即存在y1 < y < y2使得(x,y1)和(x,y2)都是黑点)。

解题思路

可以把平行于坐标轴的黑点互相连接起来,就能把问题转换为求交点的个数+原先黑点的个数
然后想到利用扫描线(平行于y轴)的性质,在平行于x轴的线段,当扫描线至左端点加1,右端点减1
在平行于y轴的线段时,则查询[y1, y2]区间的区间和
因为区间和,可以联想到树状数组,或线段树,而这道题只是涉及单点更新,以及区间查询,所以用树状数组(下标是y)再好不过了
注:1.先将其进行离散化
  2.对于线段赋值时,要注意,是横线时,它的左端点和右端点都要赋值,只不过它们的y1和y2相同

AC代码:

#include <bits/stdc++.h>
#define lowbit(x) x & (-x)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 2e5 + 10;
int n;
struct point {
    int x, y;
} p[maxn];
bool cmpx(point a, point b) {
    
    if (a.x != b.x) return a.x < b.x;
    else return a.y < b.y;
}
bool cmpy(point a, point b) {
    if (a.y != b.y) return a.y < b.y;
    else return a.x < b.x;
}
int x[maxn], y[maxn];
struct L {
    int x, y1, y2, shape, state; //shape为0为横线,shape为1为竖线
} line[maxn << 1];
bool cmpl(L A, L B) {
    return A.x == B.x ? A.y1 < B.y1 : A.x < B.x;
}
int cnt = 0;
void init() {
    sort (p + 1, p + n + 1, cmpx);//x从小到大排序,当x相同时,y从小到大排序
    for (int i = 1; i <= n; i++)
        if (p[i].x == p[i - 1].x) //说明是竖线
            line[++cnt] = (L) {p[i].x, p[i - 1].y, p[i].y, 1, 0};//此时为竖线,所以state无需赋值
    sort (p + 1, p + n + 1, cmpy);//y从小到大排序,当y相同时,x从小到大排序
    for (int i = 1; i <= n; i++)
        if (p[i].y == p[i - 1].y) //说明是横线
            line[++cnt] = (L) {p[i - 1].x, p[i].y, p[i].y, 0, 1}, //左端点加1,左端点state为1
            line[++cnt] = (L) {p[i].x, p[i].y, p[i].y, 0, -1}; //右端点减1,右端点state为-1
}
int c[maxn];
int query(int x) {
    int res = 0;
    while (x) res += c[x], x -= lowbit(x);
    return res;
}
void update(int x, int y) {
    while (x <= maxn) c[x] += y, x += lowbit(x);
}
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> p[i].x >> p[i].y;
        x[i] = p[i].x, y[i] = p[i].y;
    }
    sort (x + 1, x + n + 1);
    sort (y + 1, y + n + 1);
    int cntx = unique(x + 1, x + n + 1) - x;
    int cnty = unique(y + 1, y + n + 1) - y;
    for (int i = 1; i <= n; i++)//对x和y进行离散化
        p[i].x = lower_bound(x + 1, x + cntx + 1, p[i].x) - x, 
        p[i].y = lower_bound(y + 1, y + cnty + 1, p[i].y) - y;  
    init();//这里对线段进行赋值
    sort (line + 1, line + cnt + 1, cmpl);
    ll ans = n;
    for (int i = 1; i <= cnt; i++)
        if (line[i].shape == 0) update(line[i].y1, line[i].state);
        else ans += query(line[i].y2 - 1) - query(line[i].y1);
        //这样处理是因为,可能会有本身就有符合条件的黑点
    cout << ans << endl;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值