首页 > 其他分享 >洛谷 P6785 [COCI2013-2014#6] KRUŽNICE

洛谷 P6785 [COCI2013-2014#6] KRUŽNICE

时间:2024-02-22 21:34:55浏览次数:25  
标签:GCC 洛谷 idx int tree COCI2013 KRU pragma return

COCI的题。

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

  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;
}

单调栈 89ms (目前最优解 rank1

//#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 Circle{
	int l, r;
	bool operator < (const Circle & o) const {
		if (r == o.r) return l > o.l;
		return r < o.r;
	}
} cir[300010];

int stack[300010], top;

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){
		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;
		if (dsum == cir[i].r - cir[i].l) ++ans;
	}
	
	write(ans);
	return 0;
}

标签:GCC,洛谷,idx,int,tree,COCI2013,KRU,pragma,return
From: https://www.cnblogs.com/XuYueming/p/18028255

相关文章

  • 洛谷题单指南-贪心-P3817 小A的糖果
    原题链接:https://www.luogu.com.cn/problem/P3817题意分析:吃最少的糖果,保证相邻糖果数之和不大于x,需要某种贪心策略。解题思路:依次遍历相邻两盒糖果如果糖果数之和大于x,必须要吃点一部分,使得糖果数之和刚好等于x贪心策略是:优先吃后一盒糖果,因为这样可以更利于后续的判断成立......
  • 洛谷题单指南-贪心-P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G
    原题链接:https://www.luogu.com.cn/problem/P1090题意解读:两两合并,是典型的哈夫曼编码算法思想,贪心即可。解题思路:要是合并体力消耗最少,就要让尽可能少的果子越晚合并越好,因此,贪心策略为优先选择数量最少的两堆果子合并,一直到剩下一堆果子,把合并过程中的消耗值累加即可,要快速......
  • 洛谷题单指南-贪心-P1803 凌乱的yyy / 线段覆盖
    原题链接:https://www.luogu.com.cn/problem/P1803题意解读:通过某种贪心策略,使得能参加的比赛数越多越好。解题思路:将比赛按照结束时间由小到大哦排序,贪心策略是优先选择结束时间早的比赛,因为这样能保证后面参加更多其他比赛100分代码:#include<bits/stdc++.h>usingnamespa......
  • 洛谷题单指南-贪心-P1223 排队接水
    原题链接:https://www.luogu.com.cn/problem/P1223题意解读:第i个人接水时,后面的n-i个人就要等待,要使平均等待时间最短,即总等待时间最短,贪心法解题。解题思路:设一共n个人,第i人的接水时间为ti总等待时间为:t1*(n-1)+t2*(n-2)+...+tn直观上,贪心策略应该是让接水时间短的人在前,后面......
  • #根号分治,分块,dfs序#洛谷 7710 [Ynoi2077] stdmxeypz
    题目传送门分析首先把距离变成深度,用dfs序转成区间问题,考虑分块,散块直接改问题是整块,如果模数比较大,可以以深度为第一维下标差分标记,这样查询时就可以前缀和知道答案如果模数比较小,那么给该块打标记,查询时枚举模数即可,然后块长取1000,模数阈值取300,就能尽量减少时间代码#in......
  • 洛谷 【数据结构1-1】线性表
    P3156【深基15.例1】询问学号-洛谷|计算机科学教育新生态(luogu.com.cn)#include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;constintINF=0x3f3f3f3f;intn,m;map<int,int>mp;signedmain(){ios::sync_with_stdio(false);cin.tie(0);c......
  • 洛谷题单指南-递推与递归-P1498 南蛮图腾
    原题链接:https://www.luogu.com.cn/problem/P1498题意解读:观察样例,可以发现,当n=1时,得到最基础的图案:/\/__\当n=2时,将基础图案向下复制两个,并排,然后将之前的图案移到居中的位置/\/__\/\/\/__\/__\当n=3时,再将n=2时的图案向下复制两个,并排,然后将之前的图......
  • [洛谷P3503][POI2010][BZOI2086]Blocks
    先看数据范围,n≤1e7,k≤1e9,暴力显然行不通,只能考虑单调栈;首先题目中说每一个数都要大于k,那么我们可以在初始化时就将每一个数都减去k,将问题转化为从正数中取出数加到负数里;然后维护一个前缀和,来判断一个区间是否符合要求;显然,当sum[j]-sum[i]≥0时,区间[i+1,j]符合题意,......
  • 洛谷 P6610 [Code+#7] 同余方程
    题目描述给出若干组正整数\(p\)和\(x\),求方程\(a^2+b^2\equivx\pmodp\)关于\(a\)和\(b\)在模\(p\)意义下解的组数,其中\(p\)是奇数,且不包含平方因子题解来整一个更注重于观察结构而不是计算的题解(首先使用CRT将问题转化为模奇质数的结果相乘是显然的......
  • #根号分治,分块#洛谷 5309 [Ynoi2011] 初始化
    题目传送门分析如果\(x\)比较大那么可以暴力修改,\(x\)比较小的话可以用数组打标记查询的时候对于暴力修改的部分可以分块,暴力修改的同时需要给块打标记如果\(x\)比较小的情况,一段区间相当于是中间很多段周期加上前后缀(当然可以直接区间减但是我被卡常了)我调的块长是160......