3994: 光线追踪(raytracing)

题目描述

 

考虑一个二维平面,摄像机在(0,0)(0,0)的位置,初始时平面上没有障碍物。现在执行QQ次操作,操作有两种(假设这是第ii次操作,1≤i≤Q1≤i≤Q):

1、给定x0,y0,x1,y1(x0<x1,y0<y1)x0,y0,x1,y1(x0<x1,y0<y1),创建一个每条边与坐标轴平行的长方形障碍物,包含所有满足x0≤x≤x1x0≤x≤x1且y0≤y≤y1y0≤y≤y1的点(x,y)(x,y)(如果这个区域的某一部分已经存在障碍,则直接覆盖掉它,具体请看样例)。这个障碍物的编号为ii。

2、给定向量(x,y)(x,y),会有一个动点从摄像机所在的(0,0)(0,0)位置出发,以(x,y)(x,y)所指的方向前进,直到碰到第一个障碍物为止。

对于第2种操作,输出最先碰到的障碍物的编号。若不会碰到任何障碍物,输出0。

 

输入

 

 

输入文件第一行一个正整数QQ,表示操作总数。

接下来的QQ行,每行第一个正整数opiopi为操作种类(保证为1或2)。如果为1,则接下来四个正整数x0,y0,x1,y1(x0<x1,y0<y1)x0,y0,x1,y1(x0<x1,y0<y1)表示障碍的位置;如果为2,则接下来两个正整数x,yx,y表示前进方向。

 

 

输出

 

 

输出文件包含RR行(RR为第2种操作的总数),每行一个正整数,表示第一个碰到的障碍物编号。

 

 

样例输入

<span style="color:#333333"><span style="color:#333333">10
1 3 3 10 4
1 4 2 5 6
2 6 2
1 2 8 4 10
1 0 6 3 9
2 5 2
2 8 6
2 2 9
2 4 7
1 5 7 10 10</span></span>

样例输出

<span style="color:#333333"><span style="color:#333333">1
2
2
5
0</span></span>

提示

 

样例2

样例解释

在9次操作之后,平面的一部分如图所示(箭头为所有第2种操作询问的路线)。

数据范围

对于30% 的数据:Q≤1000Q≤1000。

对于另外30% 的数据:0≤x0,y0,x1,y1,x,y≤2000≤x0,y0,x1,y1,x,y≤200。

对于100% 的数据:Q≤105,0≤x0,y0,x1,y1,x,y≤109,x0<x1,y0<y1Q≤105,0≤x0,y0,x1,y1,x,y≤109,x0<x1,y0<y1;x0x0和y0y0不全为0,xx和yy不全为0。

 

来源

noip2018模拟-南外

题解:

一个障碍物可以挡住一个范围以内的斜率,依照这个,我们可以以斜率为下标建立一个线段树,里面维护的是斜率为k第一个遇到的障碍物编号。然而我们可能交一个障碍物于侧面或者下面,所以要把横纵坐标分开维护,开两个线段树维护,具体实现比较繁琐,代码实现能力有待提高。。。。。。。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<map>
#define eps 1e-6
using namespace std;
typedef double db;
map<db,int>dk;
struct Node{int op,x0,y0,x2,y2;}s[100005];
struct node{int l,r,id;double Min;}tre1[1200005],tre2[1200005];
//1 x 2 y
int Q,tot,cnt,iid,mx,my;double xl[500005],vv;
void build(int k,int l,int r)
{
    tre1[k].l=tre2[k].l=l;tre1[k].r=tre2[k].r=r;tre1[k].Min=tre2[k].Min=1e18;
    if(l==r)return;
    int mid=l+r>>1;
    build(k*2,l,mid);build(k*2+1,mid+1,r);
}
void update1(int k,int l,int r,int v,int id)
{
    if(l<=tre1[k].l&&r>=tre1[k].r)
    {
        if(v<=tre1[k].Min)tre1[k].Min=v,tre1[k].id=id;
        return;
    }
    int mid=tre1[k].l+tre1[k].r>>1;
    if(l<=mid)update1(k*2,l,r,v,id);
    if(r>mid)update1(k*2+1,l,r,v,id);
}
void update2(int k,int l,int r,int v,int id)
{
    if(l<=tre2[k].l&&r>=tre2[k].r)
    {
        if(v<=tre2[k].Min)tre2[k].Min=v,tre2[k].id=id;
        return;
    }
    int mid=tre2[k].l+tre2[k].r>>1;
    if(l<=mid)update2(k*2,l,r,v,id);
    if(r>mid)update2(k*2+1,l,r,v,id);
}
void ask1(int k,int t)
{
    if(tre1[k].Min<vv)vv=tre1[k].Min,iid=tre1[k].id;
    else if(tre1[k].Min==vv)iid=max(iid,tre1[k].id);
    if(tre1[k].l==tre1[k].r)return;
    int mid=tre1[k].l+tre1[k].r>>1;
    if(t<=mid)ask1(k*2,t);
    else ask1(k*2+1,t);
}
void ask2(int k,int t)
{
    if(tre2[k].Min<vv)vv=tre2[k].Min,iid=tre2[k].id;
    else if(tre2[k].Min==vv)iid=max(iid,tre2[k].id);
    if(tre2[k].l==tre2[k].r)return;
    int mid=tre2[k].l+tre2[k].r>>1;
    if(t<=mid)ask2(k*2,t);
    else ask2(k*2+1,t);
}
int main()
{
    scanf("%d",&Q);
    for(int i=1;i<=Q;++i)
    {
        scanf("%d",&s[i].op);
        if(s[i].op==1)
        {
            scanf("%d%d%d%d",&s[i].x0,&s[i].y0,&s[i].x2,&s[i].y2);
            xl[++tot]=atan2(s[i].y0,s[i].x0);
            xl[++tot]=atan2(s[i].y0,s[i].x2);
            xl[++tot]=atan2(s[i].y2,s[i].x0);
        }
        else
        {
            scanf("%d%d",&s[i].x0,&s[i].y0);
            xl[++tot]=atan2(s[i].y0,s[i].x0);
        }
    }
     
    sort(xl+1,xl+1+tot);
    for(int i=1;i<=tot;++i)if(!dk[xl[i]])dk[xl[i]]=++cnt;
    build(1,1,cnt);
    for(int i=1;i<=Q;++i)
    {
        if(s[i].op==1)
        {
            double t1,t2,t3;
            t1=dk[atan2(s[i].y0,s[i].x0)];t2=dk[atan2(s[i].y0,s[i].x2)];
            t3=dk[atan2(s[i].y2,s[i].x0)];
            update1(1,t1,t3,s[i].x0,i);
            update2(1,t2,t1,s[i].y0,i);
            if(!s[i].x0)
            {
                if(!mx)mx=i;
                if(s[i].y0<=s[mx].y0)mx=i;
            }
            if(!s[i].y0)
            {
                if(!my)my=i;
                if(s[i].x0<=s[my].x0)my=i;
            }
        }
        else
        {
            if(!s[i].x0||!s[i].y0)
            {
                if(!s[i].x0&&s[i].y0)printf("%d\n",mx);
                else if(s[i].x0&&!s[i].y0)printf("%d\n",my);
                else printf("%d\n",max(mx,my));
                continue;
            }
            double t=dk[atan2(s[i].y0,s[i].x0)];
            int iid1,iid2;double xx,kk=(db)s[i].y0/s[i].x0;
            vv=1e18;iid=0;ask1(1,t);
            iid1=iid;xx=(db)vv;
            vv=1e18;iid=0;ask2(1,t);
            iid2=iid;
            if(!iid1||!iid2){printf("%d\n",iid1+iid2);continue;}
            int ans;
            vv/=kk;
            /*if(vv-xx>-eps)ans=iid1;
            else if(xx-vv>-eps)ans=iid2;
            else ans=max(iid1,iid2);*/
            if(vv>xx||(fabs(vv-xx)<eps&&iid1>iid2)) ans=iid1;
            else ans=iid2;
            printf("%d\n",ans);
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值