首页 > 技术文章 > 2017-2018 ACM-ICPC Northern Eurasia(A.Archery Tournament)

zpj61 2021-01-27 16:20 原文

题目链接:https://vjudge.net/problem/Gym-101630A

题意: n个事件,t=1 代表增加一个圆心为(x,y),半径为 abs(y)的靶子,t=2,代表射击的坐标为(x,y)并且询问是否在已出现的靶子上,如果在则输出第几个事件的编号,否则输出-1。

该题有个坑点:(一开始没看到)一个靶子只能被射击一次。

思路:可以用set存每个靶子的位置,但是需要对靶子按照前后空隙大小进行排序,这样之后我们就可以通过射击点的横坐标对set二分查找出最靠近其的靶子,然后就可以往后遍历是否存在一个靶子使得子弹在上面,但是光这样做在P5 就T了,仔细想了想发现,我们之前排序的作用,一旦我们的 x<x1-abs(y) 那么后面的靶子也不用再遍历了,直接退出循环,这样就会减少很多不必要的操作。

然后还有简单的题解是通过线段树维护暴搜就好了:https://blog.csdn.net/lzc504603913/article/details/83958171

代码:

 

int n;
struct node{
    ll x,y,id;
    bool operator<(const node a) const {
        return x+abs(y)<a.x-abs(a.y); //排序方法 
    }
};
bool dis(ll x1,ll y1,ll a,ll b)
{
    return (x1-a)*(x1-a)+(y1-b)*(y1-b)<y1*y1;
}
set<node>s;
void run()
{
    scanf("%d",&n);
    ll t,a,b;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld%lld",&t,&a,&b);
        if(t==1)
        {   
            node Node;
            Node.x=a;
            Node.y=b;
            Node.id=i;
            s.insert(Node);
        }
        else{
            node tmp;
            bool flag=0;
            tmp.x=a;
            tmp.y=0;
            set<node>::iterator it=s.lower_bound(tmp);
            while(it!=s.end())
            {   
                ll x1=it->x,y1=it->y;
                
                if(dis(x1,y1,a,b))
                {
                    flag=true;
                    printf("%lld\n",it->id);
                    s.erase(it);
                    break;
                }
                if(it->x-a>abs(it->y)) break;//关键2; 
                it++;
            }
            if(!flag) puts("-1");
        }
    }
}
signed main()
{    
//  int t=rd();
//  while(t--) 
    run();
    return 0;
}

 

线段树的方法:

#include<iostream>
#include<deque>
#include<memory.h>
#include<stdio.h>
#include<map>
#include<string>
#include<algorithm>
#include<vector>
#include<math.h>
#include<stack>
#include<queue>
#include<bitset>
#include<set>
#define INF (0x3f3f3f3f)
using namespace std;
typedef long long int ll;
const int MAXN=200010;
 
ll X[MAXN];
ll Y[MAXN];
int tot;
vector<int> V[MAXN*30];
int ls[MAXN*30];
int rs[MAXN*30];
int ans;
 
bool check(ll x1,ll y1,ll x2,ll y2){
    if((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)<y1*y1)
        return 1;
    return 0;
}
 
void update(int L,int R,int C,int l,int r,int &rt){
 
    if(!rt)
        rt=++tot;
    if(L<=l&&r<=R)
    {
        V[rt].push_back(C);
        return;
    }
    int m=(l+r)>>1;
    if(L<=m)
        update(L,R,C,l,m,ls[rt]);
    if(R>m)
        update(L,R,C,m+1,r,rs[rt]);
}
 
void update(int L,int R,int l,int r,int rt){
    if(L<=l&&r<=R){
        vector<int> tmp;
        for(auto &t:V[rt]){
            if(t!=ans){
                tmp.push_back(t);
            }
        }
        V[rt]=tmp;
        return;
    }
    int m=(l+r)>>1;
    if(L<=m)
        update(L,R,l,m,ls[rt]);
    if(R>m)
        update(L,R,m+1,r,rs[rt]);
}
 
void query(int L,int R,int l,int r,int rt){
    if(!rt)
        return;
    for(auto &t:V[rt]){
        if(check(X[t],Y[t],L,R)){
            ans=t;
            return;
        }
    }
    if(l==r)
        return;
    int m=(l+r)>>1;
    if(L<=m)
        query(L,R,l,m,ls[rt]);
    else
        query(L,R,m+1,r,rs[rt]);
}
 
 
int main(){
 
    int N;
    scanf("%d",&N);
    int rt=0;
    for(int i=1;i<=N;i++){
 
        int op,x,y;
        scanf("%d%d%d",&op,&x,&y);
        if(op==1){
            X[i]=x;
            Y[i]=y;
            update(x-y,x+y,i,-INF,INF,rt);
        }
        else{
            ans=-1;
            query(x,y,-INF,INF,rt);
            printf("%d\n",ans);
            if(ans!=-1)
                update(X[ans]-Y[ans],X[ans]+Y[ans],-INF,INF,rt);
        }
    }
 
 
    return 0;
}
 
 
 
 
 

 

推荐阅读