李超线段树

先我来说说李超线段树能干嘛: bzoj 3165

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

我们定义一个名称“较优线段”,表示在一个区间内有两条线段,从上面看哪一条直线占有较多的位置(即在区间中点处较高的线段)。

那怎么用线段树维护呢?
我们可以这样:

  • 加入一条线段时,先找到该线段完全覆盖的区间,根据线段树的区间查询的复杂度可以知道最多有 logn 个区间;
    • 若该区间没有线段,则加入该线段,返回;
    • 若该区间有线段,则比较哪条线段为较优线段;
      • 可以通过比较交点与区间中点的位置来判断;
    • 较优线段留下,另一条线段判断一下从上往下可以看到的线段位于区间中点的左边/右边,然后把它下传到左/右子区间,重复前一步;
  • 查询时,和普通线段树一样,找到所在的位置,返回时对每个经过区间所要查询的位置计算一下高度,判断一下大小关系即可。

bzoj 3165
#include <cstdio>
#include <cstring>
#define Max(_A, _B) (_A > _B ? _A : _B)
#define Abs(_A) ((_A) > 0 ? _A : -(_A))
#define R register
#define eps 1e-9
int n, lastans;
struct Data{ int X0, X1; double Y0, Y1; int Id; } A[1 << 20], B[110];
double Calc(R int P, R Data t)
{
    if(t.X0 == t.X1) return Max(t.Y0, t.Y1);
    R double u = (t.Y1 - t.Y0) / (t.X1 - t.X0 + eps);
    u = (double) (P - t.X0) * u + t.Y0;
    return u;
}
template <class Au>
void Swap(R Au &A, R Au &B){ R Au t = A; A = B; B = t; }
void Updata(R int node, R int begin, R int end, R Data K)
{
    if(begin > end) return ;
    R int mid = begin + end >> 1;   
    if(K.X0 <= begin && end <= K.X1)
    {
        R double t1 = Calc(begin, K), t2 = Calc(end, K), t3 = Calc(begin, A[node]), t4 = Calc(end, A[node]);
        if(!A[node].Id || ((t1 > t3) && (t2 > t4))) A[node] = K;
        else if((t1 > t3) ^ (t2 > t4))
        {
            R double u = Calc(mid, K), v = Calc(mid, A[node]);
            if(u > v) 
            {
                Swap(A[node], K);
                Swap(t1, t3);
                Swap(t2, t4);
            }
            if(begin < end)
            {
                if(t3 < t1) Updata(node << 1, begin, mid, K);
                else Updata(node << 1 | 1, mid + 1, end, K);
            }
        }
        return ;
    }
    if(K.X0 <= mid) Updata(node << 1, begin, mid, K);
    if(K.X1 > mid) Updata(node << 1 | 1, mid + 1, end, K);
}
double Tmp;
void Query(R int node, R int begin, R int end, R int Pos)
{
    if(begin > end) return ;
    if(begin == end)
    {
        lastans = A[node].Id, Tmp = Calc(Pos, A[node]);
        return ;
    }
    R int mid = begin + end >> 1;
    if(Pos <= mid) Query(node << 1, begin, mid, Pos);
    else Query(node << 1 | 1, mid + 1, end, Pos);
    R double t = Calc(Pos, A[node]);
    if(t - eps > Tmp || (Abs(t - Tmp) < 1e-7 && A[node].Id < lastans)) Tmp = t, lastans = A[node].Id;
}
int main()
{
    scanf("%d", &n);
    R int cnt = 0;
    for(R int i = 1; i <= n; i++)
    {
        R int opt;
        scanf("%d", &opt);
        if(opt)
        {
            R int X0, Y0, X1, Y1;
            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) Updata(1, 1, 39989, (Data){X1, X0, Y1, Y0, ++cnt});
            else Updata(1, 1, 39989, (Data){X0, X1, Y0, Y1, ++cnt});
        }
        else
        {
            R int k;
            scanf("%d", &k);
            k = (k + lastans - 1) % 39989 + 1;
            Tmp = 0;
            Query(1, 1, 39989, k);
            printf("%d\n", lastans);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值