[BZOJ3165][Heoi2013]Segment

 题意

 在平面直角坐标系中维护两个操作:

 1.在平面上加入一条线段。记第i条被插入的线段的标号为i。

 2.询问与x=k的线段中,y 坐标最靠上的线段标号。

 (强制在线)

分析

 李超线段树模板题

 

代码

#include<iostream>
using namespace std;
const int mod1=39989,mod2=1e9,N=1e5+5;
int m,cnt,v[N<<2],ans;
struct data{
    double k,b;
    data(){}
    data(int x1,int y1,int x2,int y2){//计算直线(线段)的 k,b 
        if(x1==x2)k=0,b=max(y1,y2);
        else k=1.0*(y1-y2)/(x1-x2),b=-k*x1+y1;
    }
    double calc(int x){//计算y的值 
        return k*x+b;
    }
}t[N];
void query(int id,int l,int r,int x){//查询 
    if(t[ans].calc(x)<t[v[id]].calc(x))ans=v[id];//取最大的y 
    else if(t[ans].calc(x)==t[v[id]].calc(x)&&ans>v[id])ans=v[id];//取最小的标号 
    if(l==r)return;
    int mid=(l+r)>>1;
    if(x<=mid)query(id<<1,l,mid,x);//更新 
    else query(id<<1|1,mid+1,r,x);
}
void ins(int id,int l,int r,int x,int y,int now){//插入线段 
    if(x<=l&&r<=y){
        if(t[now].calc(l)>t[v[id]].calc(l)&&t[now].calc(r)>t[v[id]].calc(r)){//完全覆盖当前优势线段,更改 
            v[id]=now;
            return;
        }
        if(t[now].calc(l)<=t[v[id]].calc(l)&&t[now].calc(r)<=t[v[id]].calc(r))return;//完全被覆盖,gg 
        if(l==r)return;
    }
    int mid=(l+r)>>1;
    if(x<=mid)ins(id<<1,l,mid,x,y,now);//更改左边 
    if(y>mid)ins(id<<1|1,mid+1,r,x,y,now);//更改右边 
}
int main(){
    scanf("%d",&m);
    t[0].b=-1;
    int op,x1,x2,y1,y2;
    while(m--){
        scanf("%d",&op);
        if(!op){
            scanf("%d",&x1);
            x1=(x1+ans-1)%mod1+1;
            ans=0;
            query(1,1,mod1,x1);
            printf("%d\n",ans);
        }
        else{
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            x1=(x1+ans-1)%mod1+1;x2=(x2+ans-1)%mod1+1;//强制在线 
            y1=(y1+ans-1)%mod2+1;y2=(y2+ans-1)%mod2+1;
            t[++cnt]=data(x1,y1,x2,y2);
            if(x1>x2)swap(x1,x2);
            ins(1,1,mod1,x1,x2,cnt);
        }
    }
}

  

 

posted @ 2019-08-07 16:49  Evan704  阅读(103)  评论(0编辑  收藏  举报