题目大意:

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

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

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

若有多条线段符合要求,输出编号最小的线段的编号

(省略输入以及在线操作的要求)

分析:

明显的线段树特征:

1.有固定的 区间长度,(<=39989)

2.插入元素支持合并,(一个线段可以拆成两段线段)

所以我们用线段树来做这道题。

线段树维护什么元素开始不太好想。

发现要求一个交点最靠上的线段的编号,所以类似维护区间最大值,我们可以在每个区间内维护一个线段(这个线段两端坐标就是l和r,也就是说,恰好放在这个区间内),保证这个线段和x=mid这条竖直的线的交点的纵坐标是有史以来最大的。

我们在区间里记录下来这个线段的所属编号(注意不是线段编号,因为真实情况的一条线段可能会被劈成许多段,但是它们本质上还是同一个线段,在贡献答案的时候,还是要输出它们所代表的真实线段的编号的)。

同时用一个结构体记录下来每个线段的x1,x2,y1,y2以及所属编号hao。

注意由于要劈断,纵坐标不一定是整数,所以y1,y2都是double类型的。

struct duan{
int x1,x2;
double y1,y2;
int hao;
}line[N*];
struct node{
int id;
}t[*(mod1+)];
发现不需要build,pushup,pushdown,但是。。。

我们不能将区间原有线段因为中点处的值小了而“一 棒子打死”,它中点左边或者右边的值可能还会对答案产生贡献。
所以最关键的是add操作。 .特判l==r 已经到了一个点上。 如果该点之前没有维护线段,直接维护现在的线段。 如果有,选择纵坐标靠上的直线(点),纵坐标相同,选择编号较小的。 .如果没有恰好放在区间里。 如果不过mid,根据与mid关系左右查找。 如果过mid,劈两半分别查找。 .如果找到了恰好的区间 如果该区间没有维护线段,维护该线段。 如果有, ①如果中点处新的优,留下新的,把旧的中,稍微大的一半留下(全劣则淘汰),下放到对应的子区间。 ②如果中点处值相同。一般情况选择新的劈断的下放(省的建线段)。但是当新的编号较小,并且新的线段的右端点的纵坐标大于等于旧的,这时就要劈断旧的,将旧的右半部分下放。(使得mid处一定取得是新的线段,也就是编号较小的) ③如果中点处旧的优,同理。 查询的时候,直接沿着一条logn的路径查询,时刻更新最大值和ans即可。没有什么可多说的。 详见代码:(写的很丑,但是比较容易看懂)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+;
const int mod1=;
const int mod2=1e9;
int n;
int has;
int la;
int cntpool,cntdel;
int del[N];
struct duan{
int x1,x2;
double y1,y2;
int hao;
}line[N*]; inline int newnode(int x1,int x2,double y1,double y2)
{
int r=cntdel?del[cntdel--]:++cntpool;
memset(line+r,,sizeof (duan));
line[r].x1=x1,line[r].x2=x2,line[r].y1=y1,line[r].y2=y2;
return r;
}//取新节点
void dele(int x)
{
del[++cntdel]=x;
}//删除节点,回收空间 struct node{
int id;
}t[*(mod1+)];//树
double lv(int x1,int x2,double y1,double y2)
{
return (double)(y1-y2)/(1.0*x1-1.0*x2);
}//斜率
void add(int x,int l,int r,int x1,int x2,double y1,double y2,int hh)
{
if(l==r)
{
if(!t[x].id)
{
int tt=newnode(x1,x2,y1,y2);
line[tt].hao=hh;
t[x].id=tt;
}
else{
double mx1=max(y1,y2);
double mx2=max(line[t[x].id].y1,line[t[x].id].y2);
if(mx1>mx2)
{
int tt=newnode(x1,x2,y1,y2);
line[tt].hao=hh;
dele(t[x].id);
t[x].id=tt;
}
else if(mx1==mx2)
{
if(hh<line[t[x].id].hao)
{
int tt=newnode(x1,x2,y1,y2);
line[tt].hao=hh;
dele(t[x].id);
t[x].id=tt;
}
}
}
return;
}//l==r情况 int mid=(l+r)>>;
if(x1!=l||x2!=r)
{
if(x2<=mid) add(x<<,l,mid,x1,x2,y1,y2,hh);
else if(x1>mid) add(x<<|,mid+,r,x1,x2,y1,y2,hh);
else{
double k=lv(x1,x2,y1,y2);
double d=y1+((double)mid-x1)*k;
add(x<<,l,mid,x1,mid,y1,d,hh);
add(x<<|,mid+,r,mid+,x2,d+k,y2,hh);
}
}//find
else{
if(!t[x].id)
{
int tt=newnode(x1,x2,y1,y2);
line[tt].hao=hh;
t[x].id=tt;
}
else{
int p1=line[t[x].id].x1,p2=line[t[x].id].x2;
double q1=line[t[x].id].y1,q2=line[t[x].id].y2;
int oldh=line[t[x].id].hao; double k1=lv(x1,x2,y1,y2);
double k2=lv(p1,p2,q1,q2);
double d1=y1+1.0*(mid-x1)*k1;//new d在中点的纵坐标
double d2=q1+1.0*(mid-p1)*k2;//old d
if(d1>d2)//new > old
{
int tt=newnode(x1,x2,y1,y2);
line[tt].hao=hh;
dele(t[x].id);
t[x].id=tt;
if(q1<y1&&q2<y2) return;//warning!!
else if(q1>=y1)
{
add(x<<,l,mid,p1,mid,q1,d2,oldh);
}
else if(q2>=y2)
{
add(x<<|,mid+,r,mid+,p2,d2+k2,q2,oldh);
}
}
else if(d1==d2)//new = old
{
if(q1<y1)
{
add(x<<,l,mid,x1,mid,y1,d1,hh);
}
else if(q2<=y2)
{
if(hh<oldh)//move old
{
int tt=newnode(x1,x2,y1,y2);
line[tt].hao=hh;
dele(t[x].id);
t[x].id=tt;
add(x<<,l,mid,p1,mid,q1,d2,oldh);
}
else{
add(x<<|,mid+,r,mid+,x2,d1+k1,y2,hh);
}
}
}
else if(d1<d2)//new < old
{
if(y1<q1&&y2<q2) return;//warning!!
else if(y1>=q1)
{
add(x<<,l,mid,x1,mid,y1,d1,hh);
}
else if(y2>=q2)
{
add(x<<|,mid+,r,mid+,x2,d1+k1,y2,hh);
}
}
}
}//update
} int ans;
double zui;
void query(int x,int l,int r,int to)
{
if(l==r) {
if(!t[x].id) return;//warning!!
double mx=max(line[t[x].id].y1,line[t[x].id].y2);
if(zui<mx)
{
zui=mx;
ans=line[t[x].id].hao;
}
else if(zui==mx)
{
if(ans>line[t[x].id].hao)
ans=line[t[x].id].hao;
}
return;
} if(!t[x].id)
{
int mid=(l+r)>>;
if(to<=mid) query(x<<,l,mid,to);
else query(x<<|,mid+,r,to);
}
else{ int x1=line[t[x].id].x1,x2=line[t[x].id].x2;
double y1=line[t[x].id].y1,y2=line[t[x].id].y2; if(x1<=to&&to<=x2)
{
double k1=lv(x1,x2,y1,y2);
double d1=y1+((double)to-x1)*k1;
if(d1>zui)
{
zui=d1;
ans=line[t[x].id].hao;
}
else if(zui==d1)
{
if(ans>line[t[x].id].hao)
ans=line[t[x].id].hao;
}
}
int mid=(l+r)>>;
if(to<=mid) query(x<<,l,mid,to);
else query(x<<|,mid+,r,to);
}
}
int main()
{
scanf("%d",&n);
int q,a,b,c,d;
for(int i=;i<=n;i++)
{
scanf("%d",&q);
if(q&)
{
has++;
scanf("%d%d%d%d",&a,&b,&c,&d);
a=(a+la-)%mod1+;
b=(b+la-)%mod2+;
c=(c+la-)%mod1+;
d=(d+la-)%mod2+; if(a>c) swap(a,c),swap(b,d);
add(,,mod1+,a,c,b,d,has);
}
else{
scanf("%d",&a);
zui=0.0,ans=;
a=(a+la-)%mod1+;
query(,,mod1+,a);
printf("%d\n",ans);
la=ans;
}
}
return ;
}

 

upda:2018.12.27

其实这个东西叫做李超线段树

就是每个区间保留最靠上的直线,实现O(log^2n)的复杂度。

好像应用只有模板题?

Segment 李超线段树的更多相关文章

  1. 【BZOJ-3165】Segment 李超线段树(标记永久化)

    3165: [Heoi2013]Segment Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 368  Solved: 148[Submit][Sta ...

  2. Luogu P4097 [HEOI2013]Segment 李超线段树

    题目链接 \(Click\) \(Here\) 李超线段树的模板.但是因为我实在太\(Naive\)了,想象不到实现方法. 看代码就能懂的东西,放在这里用于复习. #include <bits/ ...

  3. 【洛谷P4097】Segment 李超线段树

    题目大意:维护一个二维平面,给定若干条线段,支持询问任意整数横坐标处对应的纵坐标最靠上的线段的 id,相同高度取 id 值较小的,强制在线. 题解:初步学习了李超线段树.李超线段树的核心思想在于通过标 ...

  4. BZOJ3165: [Heoi2013]Segment(李超线段树)

    题意 题目链接 Sol 李超线段树板子题.具体原理就不讲了. 一开始自己yy着写差点写自闭都快把叉积搬出来了... 后来看了下litble的写法才发现原来可以写的这么清晰简洁Orz #include& ...

  5. 【BZOJ 3165】 [Heoi2013]Segment 李超线段树

    所谓李超线段树就是解决此题一类的问题(线段覆盖查询点最大(小)),把原本计算几何的题目变成了简单的线段树,巧妙地结合了线段树的标记永久化与标记下传,在不考虑精度误差的影响下,打法应该是这样的. #in ...

  6. P4097 [HEOI2013]Segment 李超线段树

    $ \color{#0066ff}{ 题目描述 }$ 要求在平面直角坐标系下维护两个操作: 在平面上加入一条线段.记第 i 条被插入的线段的标号为 i 给定一个数 k,询问与直线 x = k 相交的线 ...

  7. BZOJ3165[Heoi2013]Segment——李超线段树

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

  8. BZOJ.3165.[HEOI2013]Segment(李超线段树)

    BZOJ 洛谷 对于线段,依旧是存斜率即可. 表示精度误差一点都不需要管啊/托腮 就我一个人看成了mod(10^9+1)吗.. //4248kb 892ms #include <cstdio&g ...

  9. 【BZOJ3165】[HEOI2013]Segment(李超线段树)

    [BZOJ3165][HEOI2013]Segment(李超线段树) 题面 BZOJ 洛谷 题解 似乎还是模板题QwQ #include<iostream> #include<cst ...

随机推荐

  1. Visual Studio 2019 Serial Keys

    Visual Studio 2019 Enterprise BF8Y8-GN2QH-T84XB-QVY3B-RC4DF Visual Studio 2019 Professional NYWVH-HT ...

  2. 保留最新N份备份目录脚本

    如下所示,在/opt/backup下是备份目录,只需要保留最新的三份备份,在此之前的备份目录都要删除. [root@syslog-ng ~]# cd /opt/backup/ [root@syslog ...

  3. qa_model

    [code=python] import os import sys import time import numpy import shelve import theano import thean ...

  4. This Android SDK requires Android Developer Toolkit version 17.0.0 or above. Current version is 10.0.0.v201102162101-104271. Please update ADT to the latest version.

    win7/xp 下面安装Android虚拟机,更新SDK后,在Eclipse preference里指向android-sdk-windows时. 出现 : This Android SDK requ ...

  5. 男神女神配——alpha阶段总结

    一.需求分析 虽然公共社交网络系统能够满足大多数高校校园用户在校园网络社交的需求,但是针对校园学习.工作和文化生活等方面的支持以及学校个性化需求方面却存在不足.利用电子校务平台的数据,设计了与真实校园 ...

  6. JQuery基础-- Ajax

    基本格式: get: $.get("url",data,function(res){   #.....   }) post: $.post("url",data ...

  7. 数组操作方法(包括es5)

    //push(); 定义:可以可向数组的末尾添加一个或更多元素,并返回新的长度. 方法:push(); 语法:数组.push(新元素1,新元素2,....,新元素x) 返回值:把指定的值添加到数组后的 ...

  8. 第七周PSP 新折线图和饼图 个人时间管理

    1.PSP DATE START-TIME END-TIME EVENT           DELTA TYPE 4.18 15.36 16.10 读构建执法 走神5min         29mi ...

  9. shell 学习笔记二

    一.break命令 break命令允许跳出所有循环(终止执行后面的所有循环). 下面的例子中,脚本进入死循环直至用户输入数字大于5.要跳出这个循环,返回到shell提示符下,就要使用break命令. ...

  10. 关于RabbitMQ Queue Argument的简介

    1.Message TTL message在队列queue中可以存活多长时间,以毫秒为单位:发布的消息在queue时间超过了你设定的时间就会被删除掉. channel.queueDeclare(&qu ...