前言

题目链接:洛谷

题目分析

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

  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. java和javac编译和运行记事本编写的代码

    演示代码如下: package com.springboot.demo; public class Hello { public static void main(String[] args) { S ...

  2. Unity 利用Cache实现边下边玩

    现在手机游戏的常规更新方案都是在启动时下载所有资源更新,游戏质量高的.用户粘性大的有底气,先安装2个G,启动再更新2个G,文件小了玩家还觉得品质不行不想玩. 最近在做微信.抖音小游戏,使用他们提供的资 ...

  3. llm-universe - 1

    Smiling & Weeping ---- 难怪春迟迟不来,原来是我把雪一读再读 一.大型语言模型(LLM)理论简介 1 大型语言模型(LLM)的概念 大语言模型(LLM,Large Lan ...

  4. 第一篇Scrum冲刺博客--原班人马打造队

    0 项目地址 点此进入 1 第一次开会/任务认领 1.1 第一次例会(2024.4.27) 第一次开会照片记录 1.2 开发认领 在查看老师在实验报告中学长的博客给了我一定的启发,我在腾讯表格中创建了 ...

  5. 使用Kubesec检查YAML文件安全

    目录 一.系统环境 二.前言 三.Kubesec简介 四.使用Kubesec检查YAML文件安全 五.总结 一.系统环境 本文主要基于Kubernetes1.22.2和Linux操作系统Ubuntu ...

  6. 在设备树中描述platform_device

    在设备树中描述platform_device 背景 在高通平台做gpio按键配置的时候,根据之前的经验,想从设备树中对应的关键字找到实际的驱动解析实现,以此加深对设备树属性配置的理解. 但是我并没有找 ...

  7. MyBase 7.1 可用的 Markdown 配置表

    背景 找到了一款Markdown 笔记本软件MyBase,7.1版本支持markdown,所以我非常喜欢,修改了自己博客的css到软件里面,瞬间变得好看了. 效果图 设置方法 "工具 - 编 ...

  8. C# pythonnet(2)_FFT傅里叶变换

    Python代码如下 import pandas as pd import numpy as np import matplotlib.pyplot as plt # 读取数据 data = pd.r ...

  9. 使用kafka作为生产者生产数据到hdfs(单节点)

    关键:查看kafka官网的userguide agent.sources = kafkaSourceagent.channels = memoryChannelagent.sinks = hdfsSi ...

  10. 基于python3 flet库的证书查看工具

    前言 基于python3 flet库实现了证书信息的提取,留作自用,如有错误欢迎指正. 正文 程序架构: 主程序 main.py 证书解析程序 certHandle.py 运行 python main ...