天神下凡
题目描述
Czy找到宝藏获得屠龙宝刀和神秘秘籍!现在他要去找经常ntr他的Jmars报仇……
Czy学会了一招“堕天一击”,他对一个地点发动堕天一击,地面上就会留下一个很大的圆坑。圆坑的周围一圈能量太过庞大,因此无法通过。所以每次czy发动技能都会把地面分割。Jmars拥有好大好大的土地,几十眼都望不到头,所以可以假设土地的大小是无限大。现在czy对他发动了猛烈的攻击,他想知道在泽宇攻击之后他的土地被切成几份了?
Czy毕竟很虚,因此圆心都在x坐标轴上。另外,保证所有圆两两之间不会相交。
输入
输入第一行为整数n,表示czy放了n次堕天一击。
接下来n行,每行两个整数x[i],r[i]。表示在坐标(x[i] , 0)放了一次堕天一击,半径为r[i]。
输出
输出一行,表示地面被分割成几块。
样例输入
4
7 5
-9 11
11 9
0 20
样例输出
6
【题解】
A掉了~但是是因为题水啊。虽然我讲了一下但是大概大佬们在我讲之前已经懂了= =。
复习一下数学高考知识(虽然众所周知的我数学偏科):圆与圆之间的五种位置关系:相交、内切、外切、相离、内含。第一种题目里排除了,剩下的所有情况正常情况下都是多一个圆就多一个区域。我还多考虑了一下重复的圆,不过不知道有没有这种数据。加上剩下的无限大土地,就是以不重复的圆的数目+1为基准。至于多出来的,都是因为有圆被多个圆内切,整个把圆切成了两半。
先把圆排序,按左边界从小到大、左边界相同的右边界从大到小,这样排主要是为了保证能内切本圆的圆都在它后面。枚举各个圆,肯定要O(n)的效率,只能尽量优化一下判断部分。刚开始想用类似单调队列的办法,发现也不是那么容易维护,就在每个点建了个堆,堆顶是目前这个圆内部最靠右的圆的右边界。这个合理性好像是可以证?因为不相交所以它就合理了,如果有圆相切它一定左边界连着堆顶。
听了ryf大佬讲题发现堆并不是那么重要,直接删掉它并没有什么关系,拿个变量维护一下就好了。大佬们讲得非常精彩。
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; const int sj=300010; int n,jg; struct ci { int x,r,zj,yj; bool cf; }c[sj]; int mp(const ci&a,const ci&b) { if(a.cf!=b.cf) return a.cf<b.cf; if(a.zj==b.zj) return a.yj>b.yj; return a.zj<b.zj; } void init() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d%d",&c[i].x,&c[i].r); c[i].zj=c[i].x-c[i].r; c[i].yj=c[i].x+c[i].r; } sort(c+1,c+n+1,mp); jg=n; for(int i=1;i<n;i++) if(c[i].zj==c[i+1].zj&&c[i].yj==c[i+1].yj) { jg--; c[i+1].cf=1; } sort(c+1,c+n+1,mp); n=jg; jg++; } int a; int main() { init(); for(int i=1;i<=n;i++) { a=c[i].zj; for(int j=i+1;j<=n;j++) { if(c[j].zj>=c[i].yj||c[j].zj!=a) break; if(c[j].yj>a) a=c[j].yj; if(a==c[i].yj) { jg++; break; } } } printf("%d",jg); return 0; }
南风知我意,吹梦到西洲。