其他分享
首页 > 其他分享> > 小奇画画——BFS

小奇画画——BFS

作者:互联网

小奇画画

题目描述

红莲清泪两行欲吐半点却无
如初是你杳然若绯雾还在水榭畔画楼处
是谁衣白衫如初谁红裳如故
——《忆红莲》

小奇想画几朵红莲,可惜它刚开始学画画,只能从画圆开始。小奇画了 \(n\) 个圆,它们的圆心都在 \(x\) 轴上,且两两不相交(可以相切)。现在小奇想知道,它画的圆把画纸分割成了多少块?(假设画纸无限大)

输入格式

第一行包括 \(1\) 个整数 \(n\)。

接下来 \(n\) 行,每行两个整数 \(x\), \(r\),表示小奇画了圆心在 \((x,0)\),半径为 \(r\) 的一个圆。

输出格式

输出一个整数表示答案。

样例

样例输入

4
7 5
-9 11
11 9
0 20

样例输出

6

数据范围与提示

对于 \(30\%\) 数据,\(n\leq 5000\);

对于 \(100\%\) 数据,\(1\leq n\leq 300000\), \(-10^9\leq x\leq 10^9\), \(1\leq r\leq 10^9\)。

思路

第一眼看到题一直没看出来能用 \(BFS\),没想到能用图论做。

很容易发现有特殊情况:

当两个小圆的半径加起来等于大圆的半径时,会多分出一块纸片,所以我们只需要求出特殊情况的次数,最后加上 \(n+1\) 就可以了。

怎么求出特殊情况的次数呢,就跟我们的图论扯上关系了,将大圆向被它包含的小圆建边,遍历它走到的点计算半径和。

首先将每个圆排序,将左端点从小到大排序,将右端点从大到小排序,这样我们就可以先遍历到大圆,建边即可。

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int maxn=3e5+50,INF=0x3f3f3f3f;
inline int read(){
	int x=0,w=1;
	char ch;
	for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}

int n;
int rd[maxn];
struct Node{
	int x,R;
	int l,r;
}a[maxn];
vector<int> vec[maxn];//存边的边表

bool cmp(Node a,Node b){//排序
	if(a.l==b.l){
		return a.r>b.r;
	}else{
		return a.l<b.l;
	}
}

int BFS(){
	int ans=0;
	queue<int> q;
	for(int i=1;i<=n;i++){
		if(rd[i]==0){
			q.push(i);
		}	
	}
	while(!q.empty()){
		int u=q.front();
		q.pop();
		int tot=0;
		for(int i=0;i<vec[u].size();i++){
			int v=vec[u][i];
			tot+=a[v].R;//计算半径和
			q.push(v);
		}
		if(tot==a[u].R){//有特殊情况
			ans++;
		}
	}
	return ans;
}

signed main(){
	n=read();
	for(int i=1;i<=n;i++){
		a[i].x=read(),a[i].R=read();
		a[i].l=a[i].x-a[i].R;//左端点
		a[i].r=a[i].x+a[i].R;//右端点
	}
	sort(a+1,a+n+1,cmp);
	stack<int> s;
	for(int i=1;i<=n;i++){
		while(!s.empty()){
			int t=s.top();
			if(a[i].r<=a[t].r){//被包含在里面
				vec[t].push_back(i);//建边
				rd[i]++;
				break;
			}
			s.pop();//不被包含在里面,出栈
		}
		s.push(i);
	}
	printf("%lld\n",BFS()+n+1);
	return 0;
}

标签:10,ch,leq,小奇,样例,BFS,int,排序,画画
来源: https://www.cnblogs.com/Rubyonly233/p/13387324.html