题目描述
秋月十分擅长对空作战。尽管如此,必要的训练也不能懈怠。
在一次训练中,会有m个事件发生。1. 出现一架飞机在(x1,y1)到(x2,y2)的线段上出现并来回飞行。
2. 秋月接到指令,要在x坐标为a处击坠一架飞行路线经过该位置的飞机。
为了确保击坠的可能性最大,秋月将选择在x位置时高度最低的飞机。
由于只是训练任务,秋月并不会真正地击坠飞机。你只需要在每次接到指令时告诉她高度最低的飞机的y坐标就可以了。
题解
这道题就要用到所谓的李超线段树, 主要的思想就是计算出每个线段的最优势线段, 最优势线段就是指, 从当前线段的x轴往上看, 暴露得最多的就是最优的, 所以说高度最低的一定是在某条又是线段里面, 每次插入维护一下就行了, 靠新开两个域, 分别是当前优势线段在当前线段 l 处的高度, 和在r处的高度, 就能判断新线段和就线段谁才是最优势线段. 查询递归取max即可. x坐标的范围是1e9的, 注意动态开空间.
上面讲的只是大概, 代码里面有详解, 很清晰, 能看懂.
#include<stdio.h>
#include<cmath>
#include<algorithm>
using namespace std;
const double eps = 1e-8;
const int lg = 30;
const int maxn = 200005;
const int inf = 1e9;
int n, id, root;
inline const int read(){
register int x = 0;
register char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x;
}
struct node{
int l, r;
double lv, rv;
}tr[maxn * lg];
inline double calc(int x, int x1, double y1, int x2, double y2){
return (y2 - y1) * (x - x1) / (x2 - x1) + y1;
}
inline void modify(int &rt, int lf, int rg, int x1, double y1, int x2, double y2){
if(!rt) rt = ++id, tr[rt].lv = inf, tr[rt].rv = inf; //没有线段, 那左右端点的最优线段的高度值初始化为
int mid = (lf + rg) >> 1;
if(x1 <= lf && rg <= x2){//如果覆盖了当前线段区间
double lv = calc(lf, x1, y1, x2, y2), rv = calc(rg, x1, y1, x2, y2); //计算新线段在左右端点的高度
double sl = lv - tr[rt].lv, sr = rv - tr[rt].rv;
if(sl <= eps && sr <= eps){ //说明了当前新插入的在当前线段(线段树区间)左右端点处的都要比原来的优势线段小,
//那么肯定暴露得最多.
tr[rt].lv = lv;//直接修改
tr[rt].rv = rv;
return;
}
if(sl >= -eps && sr >= -eps) return;//比原来的都高, 没有用
if(fabs(sl) < fabs(sr)){//这里说明新老交叉, 可以画一下, 图上是两个交叉的线段
//这里是靠一个相似三角形来判断哪个暴露的更多
if(sr <= eps){ //如果是负数的话说明当前线段优
modify(tr[rt].l, lf, mid, lf, tr[rt].lv, rg, tr[rt].rv); //先把旧的递归下去更新
//这样保证把每条线段都用的淋漓尽致
tr[rt].lv = lv; //更新当前最优线段, 如果查询的位置在这个区间里的话一定会计算这条最优线段的
tr[rt].rv = rv;//可以看看query辅助理解
} else
modify(tr[rt].l, lf, mid, x1, y1, x2, y2); //新老交点在左边,由于不如老的优,所以用武之地只在左边
} else {
if(sl <= eps){ //说明当前线段优
modify(tr[rt].r, mid + 1, rg, lf, tr[rt].lv, rg, tr[rt].rv); //同上
tr[rt].lv = lv;
tr[rt].rv = rv;
} else
modify(tr[rt].r, mid + 1, rg, x1, y1, x2, y2);
}
} else {///不然就递归处理
if(x1 <= mid)
modify(tr[rt].l, lf, mid, x1, y1, x2, y2);
if(x2 > mid)
modify(tr[rt].r, mid + 1, rg, x1, y1, x2, y2);
}
}
double query(int rt, int lf, int rg, int pos){
if(!rt) return inf;
if(lf == rg) return tr[rt].lv;
int mid = (lf + rg) >> 1;
double ans = calc(pos, lf, tr[rt].lv, rg, tr[rt].rv);//计算高度
if(pos <= mid)
return min(ans, query(tr[rt].l, lf, mid, pos));
else
return min(ans, query(tr[rt].r, mid + 1, rg, pos));
}
int main(){
freopen("defense.in", "r", stdin);
freopen("defense.out", "w", stdout);
n = read();
int x1, y1, x2, y2;
for(register int i = 1; i <= n; ++i){
int opt = read();
if(opt == 1){
x1 = read(), y1 = read(), x2 = read(), y2 = read();
modify(root, 1, inf, x1, y1, x2, y2); //插入
}else
x1 = read(), printf("%0.4lf\n", query(root, 1, inf, x1));
}
}