Typesetting math: 100%

Luogu5816 内部白点(扫描线)题解

题意

有一张由黑白点构成的网格图,给出一些黑点的坐标,求上下左右都有黑点(不必相邻)的白点数目与原来黑点数目之和。

一个说明

为什么原题面中永不终止的情况是不可能的?

对于每一个将会被染成黑色的白点,它的上方,下方,左边,右边(不必相邻)一定是都是有黑点的,而一个既不在所属列的两端,又不在所属行的两端的黑点(即它的上下左右(不必相邻)都有黑点),是不会对答案有任何贡献的。换言之,任何被染成黑点的白点,都不会再染别的白点,所以整个染色只会进行一轮。

算法

一个不像扫描线的扫描线

不会扫描线的戳我

思路

只有处于同一行的两黑点之间的部分才能染色,为了方便,我们可以只处理相邻两黑点。

先将黑点以y为第一关键字,x为第二关键字排序,对于每一列,处理出最高点与最低点,当处理到最高点时在线段树上将该列+1, 最低点时最线段树上将该列1。对于每两个同行相邻的点,区间求和即可。

代码

copy
#include <cstdio> #include <algorithm> using namespace std; const int maxn = 5e5 + 10; int b[maxn],n,ans; struct Mar{ int x,y; }m[maxn]; struct Line{ int high,low; Line(){high = 0, low = 0x3f3f3f3f;} }ll[maxn]; struct Seg_Tree{ #define lc(x) x << 1 #define rc(x) x << 1 | 1 int c[maxn << 2],tag[maxn << 2]; void f(int l, int r, int p, int x){ c[p] += (r - l + 1) * x; tag[p] = x; } void downdate(int l, int r, int p){ if(tag[p]){ int mid = (l + r) >> 1; f(l, mid, lc(p), tag[p]); f(mid + 1, r, rc(p), tag[p]); tag[p] = 0; } } void add(int L, int R, int l, int r, int p, int x){ if(L <= l && R >= r){ f(l, r, p, x); return; } downdate(l, r, p); int mid = (l + r) >> 1; if(mid >= L) add(L, R, l, mid, lc(p), x); if(mid < R) add(L, R, mid + 1, r, rc(p), x); c[p] = c[lc(p)] + c[rc(p)]; } int query(int L, int R, int l, int r, int p){ if(L <= l && R >= r){ return c[p]; } downdate(l, r, p); int mid = (l + r) >> 1, sum = 0; if(mid >= L) sum += query(L, R, l, mid, lc(p)); if(mid < R) sum += query(L, R, mid + 1, r, rc(p)); return sum; } }tree; bool cmp(Mar x, Mar y){return x.y == y.y ? x.x < y.x : x.y > y.y;} int main(){ scanf("%d", &n); for(int i = 1; i <= n; ++ i){ scanf("%d %d", &m[i].x, &m[i].y); b[i] = m[i].x; b[i + n] = m[i].y; } sort(b + 1, b + 1 + 2 * n); int _n = unique(b + 1, b + 1 + 2 * n) - b - 1; for(int i = 1; i <= n; ++ i){ int pos1 = lower_bound(b + 1, b + 1 + _n, m[i].x) - b; int pos2 = lower_bound(b + 1, b + 1 + _n, m[i].y) - b; m[i].x = pos1, m[i].y = pos2; //printf("%d %d\n", pos1, pos2); ll[m[i].x].high = max(ll[m[i].x].high, m[i].y); ll[m[i].x].low = min(ll[m[i].x].low, m[i].y); } sort(m + 1, m + 1 + n, cmp); m[n + 1].y = 0x3f3f3f3f; for(int i = 1; i <= n; ++ i){ if(m[i].y == ll[m[i].x].high) tree.add(m[i].x, m[i].x, 1, _n, 1, 1); if(m[i].y == ll[m[i].x].low) tree.add(m[i].x, m[i].x, 1, _n, 1, -1); if(m[i + 1].y == m[i].y) ans += tree.query(m[i].x + 1, m[i + 1].x - 1, 1, _n, 1); } printf("%d\n", ans + n); return 0; }
posted @   When_C  阅读(99)  评论(0编辑  收藏  举报
编辑推荐:
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
阅读排行:
· C# 13 中的新增功能实操
· Ollama本地部署大模型总结
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(4)
· langchain0.3教程:从0到1打造一个智能聊天机器人
· 用一种新的分类方法梳理设计模式的脉络
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起