【BZOJ3165】Segment(线段树)

本文介绍了一种解决二维几何问题的方法,特别是在平面直角坐标系中处理线段插入及查询最高交点的问题。通过构建数据结构来高效地进行操作,包括线段的插入和查询与特定直线相交的最高线段。文章还分享了调试过程中的关键注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

    要求在平面直角坐标系下维护两个操作:
    1.在平面上加入一条线段。记第i条被插入的线段的标号为i。
    2.给定一个数k,询问与直线 x = k相交的线段中,交点最靠上的线段的编号。

Input

    第一行一个整数n,表示共n 个操作。
    接下来n行,每行第一个数为0或1。
    若该数为 0,则后面跟着一个正整数 k,表示询问与直线 x = ((k +lastans–1)%39989+1)相交的线段中交点(包括在端点相交的情形)最靠上的线段的编号,其中%表示取余。若某条线段为直线的一部分,则视作直线与线段交于该线段y坐标最大处。若有多条线段符合要求,输出编号最小的线段的编号。
    若该数为 1,则后面跟着四个正整数 x0, y0, x 1, y 1,表示插入一条两个端点为
((x0+lastans1),(y0+lastans1)((x1+lastans1),(y1+lastans1)的线段。其中lastans为上一次询问的答案。初始时lastans=0。

Output

    对于每个 0操作,输出一行,包含一个正整数,表示交点最靠上的线段的编号。若不存在与直线相交的线段,答案为0。

Sample Input

6 
1 8 5 10 8
1 6 7 2 6
0 2
0 9
1 4 7 6 7
0 5 

Sample Output

2
0
3

Data

    对于100%的数据,1n105,1k,x0,x139989,1y0y1109

I think

    以x轴横轴建树,新加入一条线段a时,若原区间未被任何线段覆盖,则直接将线段保存在区间。若原区间已保存有线段b,则比较线段a b在该区间覆盖的大小,将覆盖较大的线段保留,另一条向子区间递归下去。
    存线段也可以用结构体存斜率和截距,计算与判断会更好写。
    这道题调了很久的bug是处理线段与询问点所在直线完全重合的情况,存在x1==x2的情况时若不加判断,那么函数get返回-nan,判断顺序错了就会WA。在modify中对重合情况的判断,条件不能是ll==rr,因为在递归过程中ll rr 有可能被改为与区间一致,实际却并不是重合的情况,因此条件是 x1[t]==x2[t]。
    调试的手速很重要。任何一个细节,无论顺序,数组大小,无意重名的变量……在看似调不出来的情况下要勇于尝试,用心思考。

Code

#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
const int mod = 39989;
const int inf = 1e9;
const int sm = 1e5+10;
const int sn = 160000;
const double eps = 1e-10;
int n,s,pre;
int x1[sm],x2[sm],yy1[sm],y2[sm],p[sn];
void Swap(int &x,int &y) { int t=x;x=y;y=t; }
double get(int t,int x) {
    if(x2[t]-x1[t]==0)return y2[t];
    return (1.0*(x-x1[t])*y2[t]+1.0*yy1[t]*(x2[t]-x))/(x2[t]-x1[t]);
}
int dcmp(double x,double y) {
    return fabs(x-y)<eps?0:(x>y)?1:-1;
}
int Max(int x,int y) { return x>y?x:y; }
char ch;
void read(int &x) {
    x=0,ch=getchar();
    while(ch>'9'||ch<'0') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void modify(int i,int l,int r,int ll,int rr,int t) {
    int m=(l+r)>>1;
    if(ll<=l&&r<=rr) {
        if(x1[t]==x2[t]){ 
            if(!p[i])p[i]=t;
            else if(Max(yy1[t],y2[t])>Max(yy1[p[i]],y2[p[i]]))
                p[i]=t;
        }
        else {
            if(!p[i]) p[i]=t;
            else {
                int ja,jb,jm;
                ja=dcmp(get(t,l),get(p[i],l))>0;
                jb=dcmp(get(t,r),get(p[i],r))>0;
                if(ja&jb) p[i]=t;
                else if(ja+jb) {
                    jm=dcmp(get(t,m),get(p[i],m))>0;
                    if(ja&jm) modify(i<<1|1,m+1,r,m+1,r,p[i]),p[i]=t;
                    else if(jb&jm) modify(i<<1,l,m,l,m,p[i]),p[i]=t;
                    else if(ja) modify(i<<1,l,m,l,m,t);
                    else modify(i<<1|1,m+1,r,m+1,r,t);
                }
            }
        }
        return ;
    }
    if(ll<=m)modify(i<<1,l,m,ll,rr,t);
    if(rr> m)modify(i<<1|1,m+1,r,ll,rr,t);
}
int chk(int a,int b,int x) {
    if(!(a*b))return a+b;
    int ys=dcmp(get(a,x),get(b,x));
    return !ys?(a<b?a:b):(ys==1?a:b);
}
int query(int i,int l,int r,int x) {
    if(l==r) return p[i];
    int m=(l+r)>>1;
    if(x<=m) return chk(p[i],query(i<<1,l,m,x),x);
    else return chk(p[i],query(i<<1|1,m+1,r,x),x);
}
int main() {
    freopen("segment.in","r",stdin);
    freopen("segment.out","w",stdout);
    read(n);
    for(int i=1,ind;i<=n;++i) {
        read(ind);
        if(ind) {
            read(x1[++s]),read(yy1[s]),read(x2[s]),read(y2[s]);
            x1[s]=(x1[s]+pre-1)%mod+1,x2[s]=(x2[s]+pre-1)%mod+1;
            yy1[s]=(yy1[s]+pre-1)%inf+1,y2[s]=(y2[s]+pre-1)%inf+1;
            if(x1[s]>x2[s]) Swap(x1[s],x2[s]),Swap(yy1[s],y2[s]);
            modify(1,1,mod,x1[s],x2[s],s);
        }
        else {  
            read(ind);ind=(ind+pre-1)%mod+1;
            printf("%d\n",pre=query(1,1,mod,ind));
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值