BZOJ3165: [Heoi2013]Segment(线段树)

传送门

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

题解:
好题啊。。

可以假设目前每个区间只有一条线段覆盖(初始化为0),考虑新加入一条边的影响:
如果这条边在原边之上或者之下,那么直接保留较上面的边就行了。

如果有交点,那么一定有一条线段覆盖了区间的一半(交点的左边或者后边),那么这个区间保留这条线段,将另一条影响较小的线段下传到子树处理子问题就好了。每条线段减小一半,那么只会影响到 logn 个区间,一次至多加入 logn 条线段,总的复杂度为 O(nlog2n)
(注意处理线段退化为点的边界情况)
查询时查找根到节点的最大值即可。

Code:

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    char ch=getchar();int i=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}
    return i*f;
} 
inline void W(int x){
    static int buf[50];
    if(!x){putchar('0');return;}
    while(x)buf[++buf[0]]=x%10,x/=10;
    while(buf[0])putchar(buf[buf[0]--]+'0');
}
const int Maxn=2e5+50,Rlim=39989;
const int LIM=1e9;
const double eps=1e-9;
inline int sgn(double x){return (x>eps)-(x<-eps);}
double ly[Maxn],ry[Maxn];
int n,p,id,ind[Maxn],L,R,yc0,yc1,cnt;
double mx;
inline double calc(int l,double lc,int r,double rc,int pos){
    if(l==r&&l==pos)return lc;
    double sp=(rc-lc)/(r-l);
    return (double)lc+sp*(pos-l);
}
inline double calc2(int l,int r,double lc1,double rc1,double lc2,double rc2){
    double sp1=(rc1-lc1)/(r-l),sp2=(rc2-lc2)/(r-l);
    return (lc2-lc1)/(sp1-sp2)+l;
}
inline void upt(int k,int l,int r,double lc,double rc,int ii){
    if(!ind[k]){ly[k]=lc,ry[k]=rc;ind[k]=ii;return;}
    if(l==r){
        if(sgn(ly[k]-lc)<0)ly[k]=(ry[k]=lc),ind[k]=ii;
        return;
    }
    int t1=sgn(ly[k]-lc),t2=sgn(ry[k]-rc);
    if((t1>=0&&t2>=0&&ind[k]<ii)||(t1>0&&t2>0))return;
    if((t1<=0&&t2<=0&&ind[k]>ii)||(t1<0&&t2<0)){ly[k]=lc,ry[k]=rc;ind[k]=ii;return;}
    double pos=calc2(l,r,lc,rc,ly[k],ry[k]);
    int mid=(l+r)>>1;
    if(sgn(pos-mid)<=0){
        if(sgn(ly[k]-lc)>=0&&sgn(ry[k]-rc)<=0){
            double v=calc(l,ly[k],r,ry[k],mid),v2=ly[k];
            ly[k]=lc,ry[k]=rc;upt(k<<1,l,mid,v2,v,ind[k]);ind[k]=ii;
        }else{
            double v=calc(l,lc,r,rc,mid),v2=lc;
            upt(k<<1,l,mid,v2,v,ii);
        }
    }else{
        if(sgn(ly[k]-lc<=0)&&sgn(ry[k]-rc)>=0){
            double v=calc(l,ly[k],r,ry[k],mid+1),v2=ry[k];
            ly[k]=lc,ry[k]=rc;upt(k<<1|1,mid+1,r,v,v2,ind[k]);ind[k]=ii;
        }else{
            double v=calc(l,lc,r,rc,mid+1),v2=rc;
            upt(k<<1|1,mid+1,r,v,v2,ii);
        }
    }
}
inline void modify(int k,int l,int r,int ii){
    if(L<=l&&r<=R){upt(k,l,r,calc(L,yc0,R,yc1,l),calc(L,yc0,R,yc1,r),ii);return;}
    int mid=(l+r)>>1;
    if(R<=mid)modify(k<<1,l,mid,ii);
    else if(L>mid)modify(k<<1|1,mid+1,r,ii);
    else modify(k<<1,l,mid,ii),modify(k<<1|1,mid+1,r,ii);
}
inline int query(int k,int l,int r,int pos){
    if(l==r){
        if(sgn(ly[k]-mx)>0||(sgn(ly[k]-mx)>=0&&ind[k]<id))mx=ly[k],id=ind[k];
        return id;
    }
    double y=calc(l,ly[k],r,ry[k],pos);
    if(sgn(y-mx)>0||(sgn(y-mx)>=0&&ind[k]<id))mx=y,id=ind[k];
    int mid=(l+r)>>1;
    return (pos<=mid)?(query(k<<1,l,mid,pos)):(query(k<<1|1,mid+1,r,pos));
}
int main(){
    n=read();
    for(int i=1;i<=n;i++){
        int op=read();
        if(op==1){
            L=(read()+p-1)%Rlim+1,yc0=(read()+p-1)%LIM+1;
            R=(read()+p-1)%Rlim+1,yc1=(read()+p-1)%LIM+1;
            if(L>R)swap(L,R),swap(yc0,yc1);
            if(L==R)yc0=(yc1=max(yc0,yc1));
            ++cnt;modify(1,1,Rlim,cnt);
        }else (mx=0,id=0,p=query(1,1,Rlim,(read()+p-1)%Rlim+1),W(p),putchar('\n'));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值