前言

题目链接:洛谷

题目分析

显然,手模样例发现答案分为以下几个贡献:

  1. 所有圆外面的那个大平面,贡献为 \(1\)。
  2. 每个圆至少被分成一部分,贡献为 \(n\)。
  3. 如果有一个圆被“拦腰截断了”,即整条直径上都被更小的圆填满了,就额外对答案贡献加 \(1\),这也是我们所求部分。

暴力跳 set

遇事不决,先打暴力;不加优化,不如跳题。一个很显然的想法,如果在处理第 \(i\) 个圆的时候,之前所有比它更小的圆都更新到平面上,那我们只需要看看是不是这个圆的直径被完整覆盖就行了。最暴力的想法就是从左端点开始,一步一步向右边走,看看有没有出现空隙。直接扫是 \(\Theta(n)\) 的,可以用一个 set 来维护当前已经加入的圆,扫描的时候用 lower_bound 来加速。具体细节不用多说,就是模拟。但是有个细节,如果存在一个圆把另一个圆包含了,那么后者可以直接从 set 里删除,因为其不会对之后的答案产生贡献了。

时间复杂度:\(\Theta(n \log n)\),每个点进出 set 一次。

线段树

其实 如果你数据结构学傻了 可以用维护区间被覆盖的最小次数,当查询时,看看这个最小次数 \(cnt\) 如果 \(cnt \geq 1\) 说明被完整覆盖了。区间加,区间查询最小值,当然使用 分块 线段树。另外地,也可以直接用一个 bool 类型的变量来表示这一个区间是不是被完整覆盖了。当然两者都需要先把所有坐标离散化掉。

时间复杂度:\(\Theta(n \log n)\),但逝常数较大 (比分块强),那有没有更优雅一点的做法呢?

并查集

区间上的问题有时可以想象左端点向右端点连边,那我们在插入一个圆的时候,把左端点连向右端点,查询的时候看看当前的左端点是不是已经和右端点连在一起了。因为如果有空隙,两个端点是不会处在同一个连通块里的。

时间复杂度:\(\Theta(n \log n)\),瓶颈在于排序,人家并查集 \(\Theta(n \alpha(n))\) 已经够优秀了,但是能不能把这个并查集的小常数优化掉呢?

单调栈

发现对于某一个圆,如果它被插入进来了,那我们就可以把里面的圆都删了,对答案不会产生影响(是不是就是上面并查集的路径压缩?其实上面可以直接暴力跳 set 的原因就是越过了已经被包含的圆,每个圆最多只被访问一次),发现还和什么很像?单调栈!求矩形面积那题思想是把所有不可能成为解的状态删除,同样在这一题我们也可以及时删除已经被包含进来的圆。具体地,如果先把所有圆按照右端点排序(按照左端点也可以),然后维护一个从栈顶到栈底左端点单调递减的单调栈,那我们能不能在弹栈的时候处理些什么呢?当然可以左右端点一个一个判断过来,但是有没有更优雅的解法呢?发现由于题目性质和单调栈性质,没有圆存在相交或者包含关系,我们只需要统计弹出的圆的直径的和是否等于当前圆的直径就可以了。

时间复杂度:\(\Theta(n \log n)\),瓶颈在于排序,单调栈是 \(\Theta(n)\) 的。

这样,这道题目就被我们解决了。我们一步步优化算法,抽丝剥茧,找到问题的本质。但是实现上的细节,注意此题的离散化,要在点的两边建空点,要不然判断是否填满会出问题。

代码 (略去快读快写)

暴力跳 set 114ms

//#pragma GCC optimize(3)
//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx", "sse2", "sse3", "sse4", "mmx")
#include <iostream>
#include <cstdio>
#define debug(a) cerr << "Line: " << __LINE__ << " " << #a << endl
#define print(a) cerr << #a << "=" << (a) << endl
#define file(a) freopen(#a".in","r",stdin), freopen(#a".out","w",stdout)
#define main Main(); signed main(){ return ios::sync_with_stdio(0), cin.tie(0), Main(); } signed Main
using namespace std; #include <algorithm>
#include <set> int n;
struct Circle{
int l, r;
bool operator < (const Circle & o) const {
if (r == o.r) return l > o.l;
return r < o.r;
}
} cir[300010]; set<pair<int, int> > S;
typedef set<pair<int, int> >::iterator Iter; bool query(int l, int r){
Iter it = S.lower_bound({l, -0x3f3f3f3f});
int now = l;
while (it != S.end()){
if (it -> first != now || it -> first > r) return false;
if (it -> second == r) return true;
Iter tmp = it; ++tmp;
now = it -> second, S.erase(it);
it = tmp;
}
return false;
} void insert(int l, int r){
Iter it = S.lower_bound({l, 0});
while (it != S.end()){
if (it -> first > r) break;
Iter tmp = it; ++tmp;
S.erase(it);
it = tmp;
}
S.insert({l, r});
} signed main(){
read(n);
for (int i = 1; i <= n; ++i) {
int x, r; read(x, r);
cir[i] = {x - r, x + r};
}
sort(cir + 1, cir + n + 1); int ans = n + 1;
for (int i = 1; i <= n; ++i){
if (query(cir[i].l, cir[i].r)) ++ans;
insert(cir[i].l, cir[i].r);
} write(ans);
return 0;
}

线段树(区间最小值) 275ms

//#pragma GCC optimize(3)
//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx", "sse2", "sse3", "sse4", "mmx")
#include <iostream>
#include <cstdio>
#define debug(a) cerr << "Line: " << __LINE__ << " " << #a << endl
#define print(a) cerr << #a << "=" << (a) << endl
#define file(a) freopen(#a".in","r",stdin), freopen(#a".out","w",stdout)
#define main Main(); signed main(){ return ios::sync_with_stdio(0), cin.tie(0), Main(); } signed Main
using namespace std; #include <algorithm> int n; struct Segment{
int l, r;
bool operator < (const Segment & o) const {
return r - l < o.r - o.l;
}
} seg[300010];
int real[300010 << 1], tot; struct Segment_Tree{
#define lson (idx << 1 )
#define rson (idx << 1 | 1) struct node{
int l, r;
int lazy;
int minn;
} tree[300010 << 3]; void build(int idx, int l, int r){
tree[idx] = {l, r, 0, 0};
if (l == r) return;
int mid = (l + r) >> 1;
build(lson, l, mid), build(rson, mid + 1, r);
} void pushtag(int idx, int v){
tree[idx].lazy += v;
tree[idx].minn += v;
} void pushup(int idx){
tree[idx].minn = min(tree[lson].minn, tree[rson].minn);
} void pushdown(int idx){
if (tree[idx].lazy == 0) return;
pushtag(lson, tree[idx].lazy), pushtag(rson, tree[idx].lazy);
tree[idx].lazy = 0;
} void modify(int idx, int l, int r){
if (tree[idx].r < l || tree[idx].l > r) return;
if (l <= tree[idx].l && tree[idx].r <= r) return pushtag(idx, 1);
pushdown(idx), modify(lson, l, r), modify(rson, l, r), pushup(idx);
} int query(int idx, int l, int r){
if (tree[idx].r < l || tree[idx].l > r) return 0x3f3f3f3f;
if (l <= tree[idx].l && tree[idx].r <= r) return tree[idx].minn;
return pushdown(idx), min(query(lson, l, r), query(rson, l, r));
} #undef lson
#undef rson
} yzh; signed main(){
read(n);
for (int i=1;i<=n;++i){
int x, r; read(x, r);
seg[i] = {x - r, x + r};
real[++tot] = x - r, real[++tot] = x + r;
}
sort(real + 1, real + tot + 1), tot = unique(real + 1, real + tot + 1) - real - 1;
for (int i=1;i<=n;++i){
seg[i].l = lower_bound(real + 1, real + tot + 1, seg[i].l) - real;
seg[i].r = lower_bound(real + 1, real + tot + 1, seg[i].r) - real - 1;
}
yzh.build(1, 1, tot); int ans = n + 1;
sort(seg + 1, seg + n + 1); for (int i=1;i<=n;++i){
if (yzh.query(1, seg[i].l, seg[i].r) > 0) ++ans;
else yzh.modify(1, seg[i].l, seg[i].r);
} write(ans);
return 0;
}

线段树(区间覆盖) 257ms

//#pragma GCC optimize(3)
//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx", "sse2", "sse3", "sse4", "mmx")
#include <iostream>
#include <cstdio>
#define debug(a) cerr << "Line: " << __LINE__ << " " << #a << endl
#define print(a) cerr << #a << "=" << (a) << endl
#define file(a) freopen(#a".in","r",stdin), freopen(#a".out","w",stdout)
#define main Main(); signed main(){ return ios::sync_with_stdio(0), cin.tie(0), Main(); } signed Main
using namespace std; #include <algorithm> int n; struct Segment{
int l, r;
bool operator < (const Segment & o) const {
return r - l < o.r - o.l;
}
} seg[300010];
int real[300010 << 1], tot; struct Segment_Tree{
#define lson (idx << 1 )
#define rson (idx << 1 | 1) struct node{
int l, r;
bool f;
} tree[300010 << 3]; void build(int idx, int l, int r){
tree[idx] = {l, r, false};
if (l == r) return;
int mid = (l + r) >> 1;
build(lson, l, mid), build(rson, mid + 1, r);
} void pushtag(int idx){
tree[idx].f = true;
} void pushup(int idx){
tree[idx].f = tree[lson].f && tree[rson].f;
} void pushdown(int idx){
if (tree[idx].f == false) return;
pushtag(lson), pushtag(rson);
} void modify(int idx, int l, int r){
if (tree[idx].r < l || tree[idx].l > r) return;
if (tree[idx].f) return;
if (l <= tree[idx].l && tree[idx].r <= r) return pushtag(idx);
pushdown(idx), modify(lson, l, r), modify(rson, l, r), pushup(idx);
} bool query(int idx, int l, int r){
if (tree[idx].r < l || tree[idx].l > r || tree[idx].f) return true;
if (l <= tree[idx].l && tree[idx].r <= r) return tree[idx].f;
return pushdown(idx), query(lson, l, r) && query(rson, l, r);
} #undef lson
#undef rson
} yzh; signed main(){
read(n);
for (int i=1;i<=n;++i){
int x, r; read(x, r);
seg[i] = {x - r, x + r};
real[++tot] = x - r, real[++tot] = x + r;
}
sort(real + 1, real + tot + 1), tot = unique(real + 1, real + tot + 1) - real - 1;
for (int i=1;i<=n;++i){
seg[i].l = lower_bound(real + 1, real + tot + 1, seg[i].l) - real;
seg[i].r = lower_bound(real + 1, real + tot + 1, seg[i].r) - real - 1;
}
yzh.build(1, 1, tot); int ans = n + 1;
sort(seg + 1, seg + n + 1); for (int i=1;i<=n;++i){
if (yzh.query(1, seg[i].l, seg[i].r) > 0) ++ans;
else yzh.modify(1, seg[i].l, seg[i].r);
} write(ans);
return 0;
}

并查集 168ms

//#pragma GCC optimize(3)
//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx", "sse2", "sse3", "sse4", "mmx")
#include <iostream>
#include <cstdio>
#define debug(a) cerr << "Line: " << __LINE__ << " " << #a << endl
#define print(a) cerr << #a << "=" << (a) << endl
#define file(a) freopen(#a".in","r",stdin), freopen(#a".out","w",stdout)
#define main Main(); signed main(){ return ios::sync_with_stdio(0), cin.tie(0), Main(); } signed Main
using namespace std; #include <algorithm> int n; struct Segment{
int l, r;
bool operator < (const Segment & o) const {
return r - l < o.r - o.l;
}
} seg[300010];
int real[300010 << 1], tot; int fa[300010];
int get(int x){
return fa[x] == x ? x : fa[x] = get(fa[x]);
} signed main(){
read(n);
for (int i=1;i<=n;++i){
int x, r; read(x, r);
seg[i] = {x - r, x + r};
real[++tot] = x - r, real[++tot] = x + r;
}
sort(real + 1, real + tot + 1), tot = unique(real + 1, real + tot + 1) - real - 1;
for (int i=1;i<=n;++i){
seg[i].l = lower_bound(real + 1, real + tot + 1, seg[i].l) - real;
seg[i].r = lower_bound(real + 1, real + tot + 1, seg[i].r) - real;
}
for (int i=1;i<=tot;++i) fa[i] = i; int ans = n + 1; for (int i=1;i<=n;++i){
int l = get(seg[i].l), r = get(seg[i].r);
if (l == r) ++ans;
else fa[l] = r;
} write(ans);
return 0;
}

单调栈 79ms 目前最优解 rank1

#include <algorithm>
#include <cstdio>
using namespace std; int n;
struct Circle{
int l, r;
inline bool operator < (const Circle & o) const {
return (r == o.r) ? (l > o.l) : (r < o.r);
}
} cir[300010]; int stack[300010], top; signed main(){
fread(buf, 1, MAX, stdin), read(n);
for (int i = 1, x, r; i <= n; ++i)
read(x), read(r), cir[i] = {x - r, x + r};
sort(cir + 1, cir + n + 1);
int ans = n + 1;
for (int i = 1; i <= n; ++i){
int dsum = 0;
while (top && cir[stack[top]].l >= cir[i].l) dsum += cir[stack[top]].r - cir[stack[top]].l, --top;
stack[++top] = i, dsum == cir[i].r - cir[i].l && ++ans;
}
printf("%d", ans);
return 0;
}

[COCI2013-2014#6] KRUŽNICE 题解的更多相关文章

  1. ZOJ Monthly, June 2014 月赛BCDEFGH题题解

    比赛链接:点击打开链接 上来先搞了f.c,,然后发现状态不正确,一下午都是脑洞大开,, 无脑wa,无脑ce...一样的错犯2次.. 硬着头皮搞了几发,最后20分钟码了一下G,不知道为什么把1直接当成不 ...

  2. Noip 2014酱油记+简要题解

    好吧,day2T1把d默认为1也是醉了,现在只能期待数据弱然后怒卡一等线吧QAQ Day0 第一次下午出发啊真是不错,才2小时左右就到了233,在车上把sao和fate补掉就到了= = 然后到宾馆之后 ...

  3. Clash Credenz 2014 Wild Card Round题解

    A题 简单模拟. /************************************************************************* > File Name: ...

  4. P6739 [BalticOI 2014 Day1] Three Friends 题解

    目录 写在前面 Solution 何为字符串哈希(可跳过): Code 写在前面 P6739 [BalticOI 2014 Day1] Three Friends 听说这题可以用比较暴力的做法过,比如 ...

  5. 2014上海全国邀请赛题解 HDOJ 5090-5099

    HDOJ 5090 水题.从小到大排序,能够填充达到符合条件的.先填充好.填充之后进行调整. 传送门:pid=5090">点击打开链接 #include <cstdio> ...

  6. 2014 百度之星题解 1002 - Disk Schedule

    Problem Description 有非常多从磁盘读取数据的需求,包含顺序读取.随机读取.为了提高效率,须要人为安排磁盘读取.然而,在现实中,这样的做法非常复杂.我们考虑一个相对简单的场景. 磁盘 ...

  7. 2014 北京邀请赛ABDHJ题解

    A. A Matrix 点击打开链接 构造,结论是从第一行開始往下产生一条曲线,使得这条区间最长且从上到下递减, #include <cstdio> #include <cstrin ...

  8. CHD 2014迎新杯比赛题解

    A. 草滩的魔法学校 分析: 高精度乘法 或 JAVA大数类 很明显 10000 的阶乘已经远远超过 64 位数能表示的范围了.所以我们要用一个比较大的数组来存放这个数.那数组要开多少位合适呢?我们不 ...

  9. 2014 百度之星 题解 1004 Labyrinth

    Problem Description 度度熊是一仅仅喜欢探险的熊,一次偶然落进了一个m*n矩阵的迷宫,该迷宫仅仅能从矩阵左上角第一个方格開始走,仅仅有走到右上角的第一个格子才算走出迷宫,每一次仅仅能 ...

  10. NOIP 2014 提高组 题解

    NOIP 2014 提高组 题解 No 1. 生活大爆炸版石头剪刀布 http://www.luogu.org/problem/show?pid=1328 这是道大水题,我都在想怎么会有人错了,没算法 ...

随机推荐

  1. AWS Ubuntu22.04安装Mysql及配置远程连接、SCP上传文件

    一.升级apt资源包 sudo apt update 二.安装MySQL sudo apt install mysql-server 三.启动MySQL服务 sudo service mysql st ...

  2. 订单号规则,不能重复。redis去重 redis集合set应用

    订单号规则,不能重复.redis去重 redis集合set应用 redis锁定商品解决并发售卖问题 RedisUtil工具类https://www.cnblogs.com/oktokeep/p/179 ...

  3. Python遥感影像叠加分析:基于一景数据提取另一数据

      本文介绍基于Python中GDAL模块,实现基于一景栅格影像,对另一景栅格影像的像元数值加以叠加提取的方法.   本文期望实现的需求为:现有一景表示6种不同植被类型的.tif格式栅格数据,以及另一 ...

  4. js-对象创建

    哥被逼得要当全栈工程师,今天练习了下各种对象的创建方式.代码较多参考了https://www.cnblogs.com/ImmortalWang/p/10517091.html 为了方便测试,整合了一个 ...

  5. CentOS7安装最新版ruby

    背景 直接通过yum安装的ruby版本太低,不能满足redis.fpm等软件的需求. 系统环境 CentOS7 安装步骤 下载ruby http://www.ruby-lang.org/en/down ...

  6. 降维(三)LLE与其他降维技术

    LLE 局部线性嵌入,Locally Linear Embedding(LLE)是另一个功能强大的非线性降维(nonlinear dimensional reduction,NLDR)技术.它是一个流 ...

  7. C#语言编写的仅有8KB大小的简易贪吃蛇开源游戏

    前言 今天大姚给大家分享一款由C#语言编写的仅有8KB大小的简易贪吃蛇开源游戏:SeeSharpSnake. 项目特点 该仓库中的项目文件和脚本可以用多种不同的配置构建相同的游戏,每个配置生成的输出大 ...

  8. pyside6 QThread 以及自定义信号 测试

    import sys import random from time import sleep from PySide6 import QtCore as qc from PySide6 import ...

  9. The Beauty of Physics

    绪言/1 学物理的人用不着对物理方程的意义操心,只要关心物理方程的美就够了. --狄拉克 此曲只应天上有--开普勒的和谐宇宙/11 天体的运动只不过是某种永恒的复调音乐而已,要用才智而不是耳朵来倾听. ...

  10. Mac mysql 5.7.x 设置服务开机自启动

    在终端输入 sudo vi /Library/LaunchDaemons/com.mysql.mysql.plist 输入以下内容 <?xml version="1.0" e ...