题目
Problem Description
DHC瘦是瘦,但有肌肉。他骨骼精奇,应堂发黑,理所当然的炼成了绝世神功“天神下凡”,DHC决定去找经常D他的KMZ报仇。KMZ拥有一片领地,几眼望不到头,所以我们认为是无限大。
DHC不敢正面和KMZ发生冲突,只敢在KMZ的领地上释放他的神功“天神下凡”,当他释放一次技能,会形成一个以目标点为圆心,半径 r 的圆形能量圈,能量圈的周围一圈有极强的能量,因此无法通过,所以圈内和圈外就被分割成了两个区域。而且能量圈也不能相交,否则会发生爆炸把DHC炸死,他当然不会犯这样的错,而内切或外切则没事。又因为DHC的神功初成,很不熟练,所以他只能以X轴上的点作为目标点。当他释放多次技能以后,地面上形成了若干个美丽的能量圈,他完全陶醉了,报仇神马的都不重要了,现在他只想知道地面被分割成了多少个区域。
Input
第一行一个正整数n,表示DHC释放了n次技能。
接下来n行,每行两个整数x,r。表示在坐标(x , 0)放了一次技能,半径为r。
1<=n<=300000,-1e9<=x<=1e9,1<=r<=1e9
Output
输出一个数,表示地面被分割成了几块。
Sample Input
4
7 5
-9 11
11 9
0 20Sample Output
6
分析
哈哈哈,时间不多了,简单说一下,先看看程序中的注释吧
程序
#include <cstdio>
#include <algorithm>
#define L(x) l[a[x]]
#define R(x) r[a[x]]
using namespace std;
int l[300010],r[300010],a[300010],b[300010],f[300010],ans,num,n;
bool cmp(int x,int y){
if (l[x]!=l[y]) return l[x]<l[y];
return r[x]>r[y];
}
//处理(排过序后)第 x 个圆里面的答案,加到 ans 里,返回值为其右边出现的第一个圆
int he(int x){
//lR记录上一个圆的右端点,F记录圆 x 是否被里面的圆分割成两半
if (f[x]) return f[x]; //f[] 既用来判断该圆是否讨论过,也用来记录他里面总共被分成了几块,所以讨论过就直接返回了
int ret=x+1,lR=L(x),F=1;
for (; L(ret)<R(x) && ret<=n; ret=he(ret)){ //把 x 包着的圆都讨论一遍(这里枚举的只是里面第一层,在里面的会被继续递归)
if (L(ret)!=lR) F=0;
lR=R(ret);
}
if (lR!=R(x)) F=0; //最后一个圆的右端点要单独判断
if (F) ans+=2; else ans+=1;
return f[x]=ret;
}
int main(){
freopen("1.txt","r",stdin);
scanf("%d",&n);
for (int i=1,xx,rr; i<=n; i++){
scanf("%d%d",&xx,&rr);
l[i]=xx-rr,r[i]=xx+rr;
}
for (int i=1; i<=n; i++) a[i]=i;
sort(a+1,a+n+1,cmp); //处理好顺序,保证数组中后面的圆不会包到前面的圆,且不会在前面的圆的左边
//还要判重,把位置大小相同的圆弄成一个
for (int i=1; i<=n; i++) if (L(i)!=L(i-1) || R(i)!=R(i-1)) b[++num]=a[i];
for (int i=1,n=num; i<=n; i++) a[i]=b[i];
for (int i=1; i<=n; i++) if (!f[i]) he(i);
printf("%d",ans+1);
}