问题 D: 天神下凡

问题 D: 天神下凡

时间限制: 3 Sec  内存限制: 256 MB
提交: 52  解决: 36

题目描述

Czy找到宝藏获得屠龙宝刀和神秘秘籍!现在他要去找经常ntr他的Jmars报仇……

Czy学会了一招“堕天一击”,他对一个地点发动堕天一击,地面上就会留下一个很大的圆坑。圆坑的周围一圈能量太过庞大,因此无法通过。所以每次czy发动技能都会把地面分割。Jmars拥有好大好大的土地,几十眼都望不到头,所以可以假设土地的大小是无限大。现在czy对他发动了猛烈的攻击,他想知道在泽宇攻击之后他的土地被切成几份了?

Czy毕竟很虚,因此圆心都在x坐标轴上。另外,保证所有圆两两之间不会相交。


输入

输入第一行为整数n,表示czy放了n次堕天一击。

接下来n行,每行两个整数x[i],r[i]。表示在坐标(x[i] , 0)放了一次堕天一击,半径为r[i]。

输出

输出一行,表示地面被分割成几块。

样例输入

47 5-9 1111 90 20

样例输出

6
 
 
复习一下数学高考知识(虽然众所周知的我数学偏科)
圆与圆之间的五种位置关系:相交、内切、外切、相离、内含
第一种题目里排除了,剩下的所有情况正常情况下都是多一个圆都多一个区域。
还多考虑了一下重复的圆,不过不知道有没有这种数据
加上剩下的无限大土地,就是以 不重复的圆的数目+1 为基准
至于多出来的,都是因为有圆被多个圆内切,整个把圆切成了两半


先把圆排序
(我是按左边界从小到大、左边界相同的右边界从大到小排的序,这样排主要是为了保证能内切
本圆的圆都在它后面)
枚举各个圆,肯定要O(n)的效率,只能尽量优化一下判断部分
刚开始想用类似单调队列的办法,发现也不是那么容易维护,就在每个点建了个堆,堆顶是目前
这个圆内部最靠右的圆的右边界
这个合理性好像是可以证?因为不相交所以它就合理了,如果有圆相切它一定左边界连着堆顶
priority_queue< int, vector<int> ,less<int> > a;
for(int i=1;i<=n;i++)
    {
      while(!a.empty()) a.pop();
      for(int j=i+1;j<=n;j++)
      {
        if(c[j].zj>=c[i].yj)  break;
        if(a.empty()||a.top()==c[j].zj)
          a.push(c[j].yj);
        if(!a.empty()&&a.top()==c[i].yj)
        {
           jg++;
           break;
        }
      }
    }
因为判断的复杂度比较玄学,最后的复杂度也不好说,比如说第一个点那个同心圆卡得比其他点慢
了好几倍
听说大佬们用的线段树,怎么做的?
 
 
 
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<queue>
#include<map>
#include<cstdlib>
#include<algorithm>
#define V 300005
#define mod 1000000007
#define LL long long
using namespace std;
int n;
map<LL, int >ma;
int sd[V*2];
priority_queue< int , vector< int > ,less< int > > a;
int id;
struct da
{
   int l,r;      
}st[V];
inline int cmp(da x,da y)
{
   if (x.l==y.l)
   return x.r>y.r; 
   return x.l<y.l;
}
inline int haha()
{
     //freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
     //freopen("treas.in","r",stdin); freopen("treas.out","w",stdout);
     int x,y;
     cin>>n;
     for ( int i=1;i<=n;i++)
     {
         //cin>>x>>y;
         scanf ( "%d%d" ,&x,&y);
         st[i].l=x-y;
         st[i].r=x+y;
         sd[0]++;
         sd[sd[0]]=x-y;
         sd[0]++;
         sd[sd[0]]=x+y;
     }
     sort(st+1,st+n+1,cmp);
     //cout<<st[1].l<<" "<<st[2].l;
     sort(sd+1,sd+sd[0]+1);
     for ( int i=1;i<=sd[0];i++)
     {
       if (!ma[sd[i]])
       {
        ma[sd[i]]=++id;
       }      
     }
     for ( int i=1;i<=n;i++)
     {
          st[i].l=ma[st[i].l];
          st[i].r=ma[st[i].r];
     }
     int jd=0;
     for ( int i=1;i<=n;i++)
     {
        while (!a.empty())a.pop();
        for ( int j=i+1;j<=n;j++)
        {
          if (st[j].l>=st[i].r)
          {
            break ;
          }
          if (a.empty()||a.top()==st[j].l)
          a.push(st[j].r);
          if (!a.empty()&&a.top()==st[i].r)
          {
            jd++;
            break ;             
          }
       
        //cout<<st[i].l<<"  "<<st[i].r<<endl;     
     }
     cout<<jd+n+1; //<<" "<<jd<<"  "<<n; 
     return 0;
}
int gg=haha();
int main()
{;}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值