2017 NEERC A Archery Tournamen(离散化+分块)

题目链接:http://codeforces.com/gym/101630/attachments/download/6401/20172018-acmicpc-northeastern-european-regional-contest-neerc-17-en.pdf

题意:

两种操作,一种是增加一个圆心为(x,y),半径为y的圆;
另一种是打出一颗击中(x,y)的子弹并要求输出打中的圆的是在第几次操作加入的,同时删除该圆,否则输出-1。
保证任意时刻没有圆交叉。

我们在CF上VP了这场比赛,然而被C题卡成了弟弟,最后我想到这题做法的时候只剩下十几分钟了,时间不够。。。场下补题的时候,找出一些乱七八糟的错误之后终于AC了

思路:

首先要考虑到,对于当前击中的(x0,y0)来说,可能击中的圆的直径一定大于y0,而对于所有直径大于y0的圆来说,要想在不交叉的情况下包含(x0,y0),那么可能的圆只有x0左右两边最接近它的两个圆,因此只需要将直径大于y0的圆中分别从左右最靠近x0的两个圆找到即可。

现在,题目转化成了如何较快的找到这两个圆。

网上有很多博客做法都是线段树,但是我当时没想到如何用线段树维护,反而想到了分块的做法。

首先,将数据全部读入,对所有的x进行离散化;  时间复杂度O(nlogn)

然后,对从1到离散化后的最大值的区间进行分块,每个块维护一个块内最大值,初值全部设为0; 时间复杂度O(n)

再然后,遍历所有操作,当操作为1的时候,将圆的信息(是第几次操作加入的、直径)存入分块的数组中x离散化后的位置,同时更新块内最大值;时间复杂度O(1)

扫描二维码关注公众号,回复: 6082788 查看本文章

当操作是2的时候,找到击中点x0离散化后的值所在的块,然后向左(向右)枚举块,直到块内最大值大于y0,再在其中找到最接近x0的圆,然后停止寻找。最后判断左右两边找到的圆是否包含了击中点,是的话,输出该圆是在第几次操作加入的,然后把要删除的圆在数组中的y值设为0,同时暴力更新该圆所在块的块内最大值,否则输出-1。时间复杂度O( sqrt(n) )

因此,总的时间复杂度是O(n sqrt(n) )。

代码:

PS:代码写得有点丑,而且写得是找大于等于y0的圆,因为虽然等于y0的圆一定无法包含(x0,y0),但是也可以保证,在它那边没有其他圆可以包含(x0,y0)

#include<bits/stdc++.h>
using namespace std;
#define For(i,a,b) for(int i=a;i<=b;i++)
#define m_p make_pair
#define p_b push_back
const int N=2e5+10;
const int M=505;
const int mod=1e9+7;
int t,n,L[M],R[M],ma[M],c[N],mp[N];
struct node{
    int opt,x,y,x1;
}a[N];
struct qaq{
    int id,y;
}b[N];
struct Ans{
    int id,x,y;
};
vector<int> s;
bool isInC(int x,int y,int xx,int yy){
    return (x-xx)*1ll*(x-xx)+(y-yy)*1ll*(y-yy)<y*1ll*y;
}
int main(){
    int k=sqrt(N);
    For(i,1,k){
        L[i]=k*(i-1)+1;
        R[i]=i*k;
    }
    if(R[k]<N){
        k++;
        L[k]=R[k-1]+1;
        R[k]=N-1;
    }
    For(i,1,k){
        For(j,L[i],R[i]){
            c[j]=i;
        }
    }
        scanf("%d",&n);
        For(i,1,n){
            scanf("%d %d %d",&a[i].opt,&a[i].x,&a[i].y);
            s.p_b(a[i].x);
        }
        sort(s.begin(),s.end());
        s.end()=unique(s.begin(),s.end());
        int pos,l,r,lpos,rpos;
        Ans lm,rm;
        For(i,1,n){
            a[i].x1=lower_bound(s.begin(),s.end(),a[i].x)-s.begin()+1;
            mp[a[i].x1]=a[i].x;
        }
        For(i,1,n){
            if(a[i].opt==1){
                b[a[i].x1].y=(a[i].y<<1);//存y的两倍 
                b[a[i].x1].id=i;
                if(b[a[i].x1].y>ma[c[a[i].x1]]) ma[c[a[i].x1]]=b[a[i].x1].y;
            }
            else{
                lm.id=rm.id=-1,l=a[i].x1-1,r=a[i].x1+1; 
                rpos=lpos=c[a[i].x1];
                while(lpos>0&&lm.id==-1){
                	if(ma[lpos]>=a[i].y){
                		l=min(R[lpos],a[i].x1);
                        while(l>=L[lpos]&&lm.id==-1){
                            if(b[l].y>=a[i].y) lm.x=l,lm.y=b[l].y,lm.id=b[l].id;
                            l--;
                        }
					}
					lpos--;
				}
				while(rpos<=k&&rm.id==-1){
                	if(ma[rpos]>=a[i].y){
                        r=max(L[rpos],a[i].x1);
                        while(r<=R[rpos]&&rm.id==-1){
                            if(b[r].y>=a[i].y) rm.x=r,rm.y=b[r].y,rm.id=b[r].id;
                            r++;
                        }
                    }
                    rpos++;
				}
                if(lm.id!=-1||rm.id!=-1){
                    if(lm.id!=-1&&isInC(mp[lm.x],lm.y>>1,a[i].x,a[i].y)){
                        pos=c[lm.x],b[lm.x].y=ma[pos]=0;
                        For(j,L[pos],R[pos]) if(b[j].y>ma[pos]) ma[pos]=b[j].y;
                        printf("%d\n",lm.id);
                    }
                    else if(rm.id!=-1&&isInC(mp[rm.x],rm.y>>1,a[i].x,a[i].y)){
                        pos=c[rm.x],b[rm.x].y=ma[pos]=0;
                        For(j,L[pos],R[pos]) if(b[j].y>ma[pos]) ma[pos]=b[j].y;
                        printf("%d\n",rm.id);
                    }
                    else printf("-1\n");
                }
                else printf("-1\n");
            }
        }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38515845/article/details/85009540