【HEOI2013】Segment

Description

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

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

  2. 给定一个数 k,询问与直线 x = k相交的线段中,交点最靠上的线段的编号。

对于 100% 的数据, 1 ≤ n ≤ 10^5, 1 ≤ k, x0, x1 ≤ 39989, 1 ≤ y0 ≤ y1 ≤ 10^9。

线段树

Get到线段树新姿势,听说叫超哥线段树(:з)∠)
线段一多就特别乱,所以对于线段树中每个线段,只保存一条优势直线
一条直线如何成为线段的优势直线?如果原来线段没有优势直线,那么直接插入就好
否则需要拿当前直线A与原有直线B进行比对,看看在当前线段中,哪条线段是大部分位置更高的那一条
//比对容易用几何方法完成,平行于y轴可以特判,不再赘述
假设A被保存,B被A替换,不妨只讨论B在[l,x] (x<=mid)中比A高
那么B存在的意义只有线段[l,mid],递归处理
其它讨论类似

考虑查询直线x=i,在线段树的线段查找到i位置的过程中,将所有出现的优势线段该位置高度取max即可

线段树真的有无限的可能,是一个功能无比强大的简洁数据结构,技巧性的东西非常多

复杂度nlog^2

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define efo(i,v) for(int i=last[v];i;i=next[i])
using namespace std;
typedef double db;
const int X=40000,N=100005;
const db eps=1e-12;
int ans,lstans,x0,y0,x1,y1,tr[X*4];
db mx;
struct line
{
    int x0,y0,x1,y1;
}a[N];
void turnx(int &x){x=((x+lstans-1)%39989+1);}
void turny(int &y){y=((y+lstans-1)%int(1e9)+1);}
db high(line a,int x)
{
    if(a.x0==a.x1) return a.y0>a.y1?a.y0:a.y1;
    db k=1.0*(a.y1-a.y0)/(a.x1-a.x0);
    return k*(x-a.x0)+a.y0;
}
db slope(line a)
{
    if(a.x0==a.x1) return 1e15;
    return 1.0*(a.y1-a.y0)/(a.x1-a.x0);
}
void getcross(line a,line b,db &x,db &y)
{
    if(a.x0==a.x1 && a.x0==b.x0 && b.x0==b.x1) x=y=0;
    else
    if(a.x0==a.x1) x=a.x0,y=high(b,x);
    else
    if(b.x0==b.x1) x=b.x0,y=high(a,x);
    else
    {
        db ka=1.0*(a.y1-a.y0)/(a.x1-a.x0),kb=1.0*(b.y1-b.y0)/(b.x1-b.x0);
        if(ka==kb) {x=-1;y=0;return;}
        x=1.0*(ka*a.x0-kb*b.x0+b.y0-a.y0)/(ka-kb);
        y=a.y0+(x-a.x0)*ka;
    }
}
void change(int v,int l,int r,int id)
{
    int mid=(l+r)>>1;
    if(a[id].x0<=l && r<=a[id].x1)
    {
        if(!tr[v])
        {
            tr[v]=id;
            return;
        }
        db xx,yy;
        getcross(a[tr[v]],a[id],xx,yy);
        bool p=slope(a[id])<slope(a[tr[v]]);
        if(xx<l-eps || xx>r+eps || l==r)
        {
            db h1=high(a[id],l),h2=high(a[tr[v]],l);
            if(h1>h2 || h1==h2 && id<tr[v]) tr[v]=id;
            return;
        }
        if(p)
        {
            if(xx>mid)
            {
                change(v+v+1,mid+1,r,tr[v]);
                tr[v]=id;
            }
            else change(v+v,l,mid,id);
        }
        else
        {
            if(xx<=mid)
            {
                change(v+v,l,mid,tr[v]);
                tr[v]=id;
            }
            else change(v+v+1,mid+1,r,id);
        }
        return;
    }
    if(a[id].x1<=mid) change(v+v,l,mid,id);
    else
    if(a[id].x0>mid) change(v+v+1,mid+1,r,id);
    else
    change(v+v,l,mid,id),change(v+v+1,mid+1,r,id);
}
void query(int v,int l,int r,int x)
{
    db h=high(a[tr[v]],x);
    if(h>mx || h==mx && tr[v]<ans) mx=h,ans=tr[v];
    if(l==r) return;
    int mid=(l+r)>>1;
    if(x<=mid) query(v+v,l,mid,x);
    else query(v+v+1,mid+1,r,x);
}
int main()
{
    int m,tp,num=0;
    scanf("%d",&m);
    fo(i,1,m)
    {
        scanf("%d",&tp);
        if(tp==0)
        {
            scanf("%d",&x0);turnx(x0);
            ans=mx=0;
            query(1,1,X,x0);
            printf("%d\n",ans);
            lstans=ans;
        }
        else
        {
            num++;
            scanf("%d %d %d %d",&a[num].x0,&a[num].y0,&a[num].x1,&a[num].y1);
            turnx(a[num].x0);turny(a[num].y0);turnx(a[num].x1);turny(a[num].y1);
            if(a[num].x0>a[num].x1) swap(a[num].x0,a[num].x1),swap(a[num].y0,a[num].y1);
            change(1,1,X,num);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值