upcoj 5725 小奇画画(map维护)

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

输入

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

输出

输出一个整数表示答案。

样例输入

4 
7 5 
-9 11 11 9 
0 20

样例输出

6

提示

对于 100%数据,1<=n<=300000,-10^9<=x<=10^9,1<=r<=10^9。

首先我们可以确定,每增加一个圆都会使答案加1。唯一要判断的是,当前的圆的直径区间是否已经被其他圆填满,如果是的话,答案还要加1。

我的思想是用map来维护一个区间是否被其他圆填满了,这个区间的左端点是l,右端点是r,如果这个区间已经被其他圆填满,那么用A[l]=r,A[r]=l表示;每当考虑一个圆的直径区间是否被其他圆填满,我们就看这个圆的左端点和右端点是否关联映射,如果是的话,答案要再加1。每考虑一个圆时,将的左右端点标记,代表这两个点可能成为切点。

维护的过程:首先将圆按照半径从小到大排序(因为判断一个圆的直径区间是否被其他圆填满,肯定是先填小圆,再看大圆)。

然后依次考虑每一个圆,先看左端点是否被标记,如果标记了,那么这个圆和先前的圆外切或内切。我们找出标记的左端点相关联的点(注意是以前的圆而不是现在的圆,A[l]=r,找出r),看它是否小于(圆从小到大排序)正考虑的圆的右端点,如果小于,那么这个圆和先前的圆内切,我们就要更新A[l]和A[r],将他扩大,因为以后的圆只能更大,不会跑到这个圆内。否则,是外切,那么也要更新点,即被其他圆填满的区间变大了。对于右端点的考虑,是一样的。

代码:

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
#include<map>
using namespace std;
typedef pair<int,int>pa;
map<int,int>M,A,C;
struct Node
{
    int x;
    int r;
} node[300005];
bool cmp(Node a,Node b)
{
    return a.r<b.r;
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        A.clear();
        C.clear();
        for(int i=1; i<=n; i++)
        {
            scanf("%d%d",&node[i].x,&node[i].r);
        }
        sort(node+1,node+n+1,cmp);//从小到大排序,先看小圆,再看大圆
        int ans=1,l,r;
        for(int i=1; i<=n; i++)
        {
            l=node[i].x-node[i].r;//圆的左端点
            r=node[i].x+node[i].r;//圆的右端点
            ans++;//每多一个圆,就会多划分一个块,答案加1
            if(A[l]==r&&A[r]==l)//左端点和右端点是否联通,即中间被其他圆相切填满
            {
                ans++;//是的话,那么这个圆被分成了两块,答案加1
             
            }
            if(!C[l]&&!C[r])//如果左右端点都没有被标记,那就将他们标记
            {
                A[l]=r;//l和r联通
                A[r]=l;
            }
            else if(C[l]&&!C[r])//左端点标记了,即这个点是之前的某个圆的左或右端点
            {
                if(A[l]<l)//是外切,将两个圆的联通区间合并
                {
                    A[r]=A[l];
                    A[A[l]]=r;
                }
                else//是内切,更新,使得联通区间更大
                {
                    A[l]=r;
                    A[r]=l;
                }
 
            }
            else if(C[r]&&!C[l])//考虑右端点,和左端点一样
            {
 
                if(A[r]>r)
                {
                    A[l]=A[r];
                    A[A[r]]=l;
                }
                else
                {
                    A[l]=r;
                    A[r]=l;
                }
 
            }
            else//左右端点都标记了,那么将这三个圆的联通区间合并
            {
                A[A[l]]=A[r];
                A[A[r]]=A[l];
            }
            C[l]=C[r]=1;//将左右端点标记
        }
        cout<<ans<<endl;
 
    }
}

猜你喜欢

转载自blog.csdn.net/zyy_1998/article/details/81272376