洛谷P4097 [HEOI2013]Segment (线段版本的李超线段树)

题意:

要求在平面直角坐标系下维护两个操作:

  1. 在平面上加入一条线段。记第 i 条被插入的线段的标号为 i
  2. 给定一个数 k,询问与直线 x = k 相交的线段中,交点最靠上的线段的编号。

思路:李超线段树

原理:线段树的节点维护的是该节点的区间中点的横坐标处y值最大的线段的标号。不需要pushup和pushdown,查询时整条链的节点都需要考虑。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100005;
const double eps = 1e-6;
int sgn( double x ){
    if( x > eps ) return 1;
    else if( x < -eps ) return -1;
    else return 0;
}
struct Line{
    int l,r,id;    // 线段的下标需要从1开始
    double yl,yr,k;
    Line(){}
    Line( int _l,int _r,int _yl,int _yr,int _id ){
        l = _l;r = _r;yl = _yl;yr = _yr;id = _id;k = ( l-r ) ? (yl-yr)/(l-r):0;
        if( l == r ) yl = yr = max( yl,yr ); //( 斜率为无穷大需要特判 )
    }
    double operator()( int x ){ return l == r?yl:yl + k*( x-l ); }
}a[maxn];
int tree[4*maxn];
bool cmp( int x,int y,int i ){
    return a[x](i) > a[y](i) ;
}
int tot = 1,ls[4*maxn],rs[4*maxn];
int query( int p,int l,int r,int x ){
    if( l == r ) {
        return tree[x];
    }
    int tmp=0;
    int mid = l+r>>1;
    if( ls[x] &&p <= mid ) tmp = query( p,l,mid,ls[x] );
    else if( rs[x] &&p > mid ) tmp = query( p,mid+1,r,rs[x] );
    if( !tmp ) return tree[x];
    else if( !tree[x] ){
        return tmp;
    }else if( tmp && tree[x] ){
        return cmp( tmp,tree[x],p ) ? tmp : tree[x];
    }else return 0;
}
void update( int k,int l,int r,int x ){
    if( a[k].l > r || a[k].r < l ) return;
    if( l == r ){
        if( !tree[x] ) {
            tree[x] = k;
        }else if( cmp( k,tree[x],l )   ) swap( tree[x],k );
        return;
    }
    int mid = l+r>>1;
    if(  a[k].l <= l && a[k].r >= r ){
        if( !tree[x] ) {
            tree[x] = k;
            return;
        }
        else {
            if( cmp( k,tree[x],mid )   ) swap( tree[x],k );
            int l1 = a[tree[x]](l),l2 = a[k](l),r1 = a[tree[x]](r),r2 = a[k](r);
            if( sgn( r1-r2 ) >= 0 && sgn( l1-l2 ) >= 0 ){
                return;
            }
            if(  sgn(l2-l1) >= 0 ){
                if(!ls[x])ls[x] = ++tot;
                update( k,l,mid,ls[x] );
            }
            if(  sgn(r2-r1) >= 0 ) {
                if(!rs[x])rs[x] = ++tot;
                update( k,mid+1,r,rs[x] );
            }
        }
    }else{
        if(!ls[x] ) ls[x] =++tot;update( k,l,mid,ls[x] );
        if(!rs[x] ) rs[x] =++tot;update( k,mid+1,r,rs[x] );
    }

}
int main(){
    int n,op,x0,y0,x1,y1,x;
    scanf("%d",&n);
    int lastans = 0,num = 0;
    for( int i = 1;i <= n;i++ ){
        scanf("%d",&op);
        if( op ){
            scanf("%d%d%d%d",&x0,&y0,&x1,&y1);
            x0 = (x0+lastans-1)%39989+1,y0 = (y0+lastans-1)%1000000000+1;
            x1 = (x1+lastans-1)%39989+1,y1 = (y1+lastans-1)%1000000000+1;
            if( x0 > x1 ){
                swap( x0,x1 );swap( y0,y1 );
            }
            a[++num] = Line( x0,x1,y0,y1,num );
            update( num ,1,39989,1 );
        }else{
            scanf("%d",&x);
            x = ((x + lastans - 1)%39989+1);
            lastans = query( x,1,39989,1 );
            printf("%d\n",lastans);
        }
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值