NEERC2017 Archery Tournament 线段树 新套路

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_37517391/article/details/80558937

题目链接

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

题意

给出一些圆,这些圆圆心在x轴上方,且与x轴相切,任意时刻,不存在图上的圆相交,给出两种操作:增加一个圆;向图中发送一颗子弹,如果击中某个圆,输出该圆编号并且删掉这个圆。如果未击中,则输出-1。

题解

圆与x轴相切,且圆之间不相交,这说明,如果先把圆抽象成平行于x轴的线段的话,那么包含子弹横坐标的线段不超过 l o g ( N ) 个,这是一个很关键的条件。
如果我们能找出这些线段,那么我们就可以进行包含判定了。

下面关键的问题在于如何找出这些线段。
这里有一个骚操作,那就是使用线段树的方式。
我们先把圆一切两半,分别考虑,先考虑子弹打在右半边的情况,左半边的情况类似。
对于圆的右半边表示成线段就是 [ x r , x r + r ] ,因为圆心不会重合,所以所有的 x r 都不相同,这也正是我们要把圆切开的原因。
将线段存在线段树里,线段树 x r 位置存放 x r + r ,代表一个线段。并且要求线段树支持区间查询最大值的操作。
下面展示如何定位到所有的包含子弹横坐标 x b 的线段。
我们采用分治的方法,初始待考虑区间为 [ 0 , x b ] (这样保证了线段的左端点 < x b ),判断区间的最大值是否大于 x b (相当于判断线段的右端点 > x b ),如果大于,说明在这个区间内有满足的线段,将区间进一步细分。
直到区间细分到长度为 1 ,停止,这就是一个满足条件的线段。

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
using namespace std;
const int maxn = 5e5+7;
int n;
int tp,x,y,len;
int segmx[maxn<<2],segmi[maxn<<2];
int al[maxn],cat = 0;
int mp[maxn];
#define pr(x) cout<<#x<<":"<<x<<endl
inline int getid(int x){
    return lower_bound(al,al+len,x) - al;
}
void updatemx(int rt,int l,int r,int pos,int x){
    if(l == r){
        segmx[rt] = x;
        return ;
    }
    int mid = (l+r)/2;
    if(pos <= mid) updatemx(rt*2,l,mid,pos,x);
    else updatemx(rt*2+1,mid+1,r,pos,x);
    segmx[rt] = max(segmx[rt*2],segmx[rt*2+1]);
}
void updatemi(int rt,int l,int r,int pos,int x){
    if(l == r){
        segmi[rt] = x;
        return ;
    }
    int mid = (l+r)/2;
    if(pos <= mid) updatemi(rt*2,l,mid,pos,x);
    else updatemi(rt*2+1,mid+1,r,pos,x);
    segmi[rt] = min(segmi[rt*2],segmi[rt*2+1]);
}

int querymx(int rt,int l,int r,int ul,int ur){
    if(ul <= l && r <= ur)
        return segmx[rt];
    if(r < ul || l > ur) return -2e9;
    int mid = (l+r)/2;
    int ansl = querymx(rt*2,l,mid,ul,ur);
    int ansr = querymx(rt*2+1,mid+1,r,ul,ur);
    return max(ansl,ansr);
}

int querymi(int rt,int l,int r,int ul,int ur){
    if(ul <= l && r <= ur)
        return segmi[rt];
    if(r < ul || l > ur) return 2e9;
    int mid = (l+r)/2;
    int ansl = querymi(rt*2,l,mid,ul,ur);
    int ansr = querymi(rt*2+1,mid+1,r,ul,ur);
    return min(ansl,ansr);
}
int tps[maxn],xs[maxn],ys[maxn];
int ans[maxn];
bool check(long long x,long long y,int id){
    long long dx = x - xs[id];
    long long dy = y - ys[id];
    if(dx*dx + dy*dy < y*y) return 1;
    return 0;
}
void dcmx(int l,int r,int id){
    int y = querymx(1,0,len-1,l,r);
    if(y <= xs[id]) return ;
    if(l == r){
        int x = al[l];
        y = y - x;
        if(check(x,y,id)) {
            ans[id] = mp[l];
            mp[l] = 0;
            updatemx(1,0,len-1,l,-2e9);
            updatemi(1,0,len-1,l,2e9);
        }
        return ;
    }
    int mid = (l+r)/2;
    dcmx(l,mid,id);
    dcmx(mid+1,r,id);

}
void dcmi(int l,int r,int id){
    int y = querymi(1,0,len-1,l,r);
    if(y >= xs[id]) return ;
    if(l == r){
        int x = al[l];
        y = -y + x;
        if(check(x,y,id)) {
            ans[id] = mp[l];
            mp[l] = 0;
            updatemi(1,0,len-1,l,2e9);
            updatemx(1,0,len-1,l,-2e9);
        }
        return ;
    }
    int mid = (l+r)/2;
    dcmi(l,mid,id);
    dcmi(mid+1,r,id);

}
int main(){
    scanf("%d",&n);

    for(int i = 1;i <= n;++i){
        scanf("%d%d%d",&tp,&x,&y);
        tps[i] = tp,xs[i] = x,ys[i] = y;
        if(tp == 1){
            al[cat++] = x;
            al[cat++] = x-y;
            al[cat++] = x+y;
        }
        else{
            al[cat++] = x;
        }
    }
    sort(al,al+cat);
    len = unique(al,al+cat) - al;

    memset(mp,0,sizeof(mp));
    for(int i = 0;i < maxn<<2;++i) segmx[i] = -2e9,segmi[i] = 2e9;
    for(int i = 1;i <= n;++i){
        int idx = getid(xs[i]);
        if(tps[i] == 1){
            mp[idx] = i;
            updatemx(1,0,len-1,getid(xs[i]),xs[i]+ys[i]);
            updatemi(1,0,len-1,getid(xs[i]),xs[i]-ys[i]);
        }
        else{
            dcmx(0,idx,i);
            dcmi(idx,len-1,i);
        }
    }

    for(int i = 1;i <= n;++i){
        if(tps[i] == 2){
            printf("%d\n",ans[i]?ans[i]:-1);
        }
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_37517391/article/details/80558937