前言

题目链接:洛谷

题目分析

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

  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. springboot项目编译时,使用自定义注解类找不到符号

    springboot项目编译时,使用自定义注解类找不到符号 Java项目编译时,使用自定义注解类找不到符号Spring-boot项目编辑器:idea问题:编译时找不到符号.项目中用到了自定义注解类.编 ...

  2. llm-universe - 1

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

  3. 新浪微博动态 RSA 分析图文+登录

    Tips:当你看到这个提示的时候,说明当前的文章是由原emlog博客系统搬迁至此的,文章发布时间已过于久远,编排和内容不一定完整,还请谅解` 新浪微博动态 RSA 分析图文+登录 日期:2016-10 ...

  4. 技嘉BIOS超频设置操作路径

    关闭超线程 频率电压控制 > 进阶处理器设置 > Hyper_THreading 关小核心 频率电压控制 > GIGABYTE PerfDrive > Ecore Disabl ...

  5. AI Agent实战:智能检索在Kingbase数据库管理中的优势应用

    前言 在信息技术飞速发展的今天,数据库管理已成为IT专业人员日常工作中不可或缺的一部分.然而,面对复杂的SQL问题,传统的web搜索往往难以提供精准的答案,尤其是在针对特定数据库系统,如金仓数据库时, ...

  6. LaravelLumen 分组求和问题 where groupBy sum

    在Laravel中使用分组求和,如果直接使用Laravel各数据库操作方法,应该会得出来如下代码式: DB::table('table_a') ->where('a','=',1) ->g ...

  7. 介绍几款强大实用的 IDEA 插件,助力大家开发【工欲善其事必先利其器】

    俗话说:"工欲善其事必先利其器",本问介绍几款强大实用的 IDEA 插件,助力大家开发. 希望大家做一个聪明又努力的人,而不只是一个努力的人. Alibaba Java Codin ...

  8. ArkTS基础知识

    [习题]ArkTS基础知识 及格分85/ 满分100   判断题 1. 循环渲染ForEach可以从数据源中迭代获取数据,并为每个数组项创建相应的组件. 正确(True)错误(False) 回答正确 ...

  9. elementplus django drf 如何做到确认单据禁止删除

    elementplus django drf 如何做到确认单据禁止删除     要在Django和Django Rest Framework(DRF)中实现禁止删除确认单据的功能,你可以通过以下步骤来 ...

  10. [rCore学习笔记 05]第0章作业题

    作业1 略. 作业2 C语言程序 gcc编译 gcc -o main main.c 编译报错 成功产生异常 main.c: In function 'main': main.c:5:26: warni ...