weinan030416

导航

扫描游戏

问题描述

有一根围绕原点 O 顺时针旋转的棒 ��OA, 初始时指向正上方 (Y 轴正向)。 在平面中有若干物件, 第 i 个物件的坐标为 (��,��)(xi,yi), 价值为 ��zi 。当棒扫到某个 物件时, 棒的长度会瞬间增长 ��zi, 且物件瞬间消失(棒的顶端恰好碰到物件也 视为扫到), 如果此时增长完的棒又额外碰到了其他物件, 也按上述方式消去 (它和上述那个点视为同时消失)。

如果将物件按照消失的时间排序, 则每个物件有一个排名, 同时消失的物 件排名相同, 请输出每个物件的排名, 如果物件永远不会消失则输出 −11 。

输入格式

输入第一行包含两个整数 �、�nL, 用一个空格分隔, 分别表示物件数量和棒 的初始长度。

接下来 n 行每行包含第三个整数 ��,��,��xi,yi,zi 。

输出格式

输出一行包含 n 整数, 相邻两个整数间用一个空格分隔, 依次表示每个物件的排名。

样例输入

5 2
0 1 1
0 3 2
4 3 5
6 8 1
-51 -33 2

样例输出

1 1 3 4 -1
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 10;
const ll INF = 9e18;
struct point
{
    ll x, y, z;
    int id;
}a[maxn];

int Quadrant(point a)
{
    if(a.x >= 0 && a.y > 0)return 1;//y的正半轴放到第一象限
    if(a.x > 0 && a.y <= 0)return 2;//x的正半轴放到第二象限
    if(a.x <= 0 && a.y < 0)return 3;
    return 4;
}
ll cross(point a, point b)//求两个点的叉积
{
    return a.x * b.y - a.y * b.x;
}
bool cmp(point a, point b)//两个点进行排序
{
    if(Quadrant(a) == Quadrant(b))//象限相同
    {
        if(cross(a, b) == 0)//方向相同
            return a.x * a.x + a.y * a.y < b.x * b.x + b.y * b.y;//按照离原点的距离排序
        else
           return cross(a, b) < 0;//方向不同,顺时针排序
    }
    else
        return Quadrant(a) < Quadrant(b);//不同象限直接排序
}

//线段树数组 
ll mi[maxn << 2];
void push_up(int o)
{
    mi[o] = min(mi[o << 1], mi[o << 1 | 1]);
}
//建树,线段树维护最小值,维护离原点的距离的平方 
void build(int o, int l, int r)
{
    if(l == r)
    {
        mi[o] = a[l].x * a[l].x + a[l].y * a[l].y;
        return ;
    }
    int mid = (l + r) >> 1;
    build(o << 1, l, mid);
    build(o << 1 | 1, mid + 1, r);
    push_up(o);
}

//更新idx=x的值为val:保证一个点第一次触碰到之后消除 
void update(int o, int l, int r, int x, ll val)
{
    if(l == r)
    {
        mi[o] = val;
        return;
    }
    int mid = (l + r) >> 1;
    if(x <= mid)update(o << 1, l, mid, x, val);
    else update(o << 1 | 1, mid + 1, r, x, val);
    push_up(o);
}


//判断节点o的值是否小于等于z^2 
inline bool check(int o, ll z)
{
    if(mi[o] == INF) return false;//此时节点o已经被消除
    if(z > 2000000000)return true;//z过大对它进行平方会溢出
    return mi[o] <= z * z;
}

///找区间[L,R]中从左往右第一个小于等于z^2的数字,找到的答案存储在idx里面 
ll idx;
bool query(int o, int l, int r, int L, int R, ll z)
{
    if(L > R)return false;
    if(l == r)
    {
        idx = l;
        return check(o, z);
    }
    int mid = (l + r) >> 1;
    if(L <= mid)
    {
        //左子树的最小值不超过z^2 
        if(check(o << 1, z) && query(o << 1, l, mid, L, R, z))
            return true;
    }
    if(R > mid)
    {
        //右子树的最小值不超过z^2 
        if(check(o << 1 | 1, z) && query(o << 1 | 1, mid + 1, r, L, R, z))
            return true;
    }
    return false;
}
int ans[maxn];

int main()
{
    ll n, L;
    cin >> n >> L;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i].x >> a[i].y >> a[i].z;
        a[i].id = i;
        ans[i] = -1;
    }
    //点按照顺时针排序 
    sort(a + 1, a + 1 + n, cmp);
    //建树 
    build(1, 1, n);
    int cnt = 0;        //当前的排名 
    int lastcnt = 0;    //上一个位置的排名 
    int last = 0;          //上一个位置的idx 
    while(true)
    {
        //查找区间[last + 1, n] 
        bool ok = query(1, 1, n, last + 1, n, L);
        if(ok)L = L + a[idx].z;
        else
        {
            //[last + 1, n]没找到,继续从区间[1, last - 1]找 
            ok = query(1, 1, n, 1, last - 1, L);
            if(ok)L = L + a[idx].z;
            else break;    //都没找到直接跳出循环 
        }
        //将idx出的点更新为INF 
        update(1, 1, n, idx, INF);
        if(last)
        {
            //如果存在上一个位置 && 上一个位置的坐标、方向和当前的一致,则排名不变 
            if(Quadrant(a[last]) == Quadrant(a[idx]) && cross(a[last], a[idx]) == 0)
                ans[a[idx].id] = lastcnt, ++cnt;
            else
                ans[a[idx].id] = ++cnt, lastcnt = cnt;
        }
        else
            ans[a[idx].id] = ++cnt, lastcnt = cnt;
        last = idx;
    }
    for(int i = 1; i <= n; i++)
    {
        cout<<ans[i];
        if(i != n)cout<<" ";
    }
    return 0;
}

 

posted on 2023-02-12 12:04  楠030416  阅读(39)  评论(0编辑  收藏  举报