Description
要求在平面直角坐标系下维护两个操作:
1.在平面上加入一条线段。记第i条被插入的线段的标号为i。
2.给定一个数k,询问与直线 x = k相交的线段中,交点最靠上的线段的编号。
Input
第一行一个整数n,表示共n 个操作。
接下来n行,每行第一个数为0或1。
若该数为 0,则后面跟着一个正整数 k,表示询问与直线
x = ((k +lastans–1)%39989+1)相交的线段中交点(包括在端点相交的情形)最靠上的线段的编号,其中%表示取余。若某条线段为直线的一部分,则视作直线与线段交于该线段y坐标最大处。若有多条线段符合要求,输出编号最小的线段的编号。
若该数为 1,则后面跟着四个正整数 x0, y0, x 1, y 1,表示插入一条两个端点为
((x0+lastans-1)%39989+1,(y0+lastans-1)%10^9+1)和((x
1+lastans-1)%39989+1,(y1+lastans-1)%10^9+1) 的线段。
其中lastans为上一次询问的答案。初始时lastans=0。
Output
对于每个 0操作,输出一行,包含一个正整数,表示交点最靠上的线段的编号。若不存在与直线相交的线段,答案为0。
Sample Input
1 8 5 10 8
1 6 7 2 6
0 2
0 9
1 4 7 6 7
0 5
Sample Output
0 3
HINT
对于100%的数据,1 ≤ n ≤ 10^5 , 1 ≤ k, x0, x1 ≤ 39989, 1 ≤ y0 ≤ y1 ≤ 10^9。
Source
这一道题非常巧妙,用一个线段树来维护了一段类似凸包的东西。这种线段树还有一个优美的名字——李超线段树(应该是一个叫李超dalao发明的QWQ?)
我们对于这一道题,用线段树的每一个节点存放一条线段:
我们这样操作:先把线段拆到log个完整的区间,对于每一个完整的区间,考虑向下递归插入当前线段:
1、如果当前线段比节点现有线段上方漏出的区间更多,那么用现有线段替换当前节点线段,将当前节点的线段递归插入到左/右儿子。
2、否则将现有线段递归插入。
对于上图来说区间AD内储存红色的线段,区间AB内储存绿色的线段,区间BC内储存蓝色的线段。类似标记永久化,那么对于一个查询,可能成为最优答案的线段一定在该节点到线段树根的路径上,我们查询的时候一边回溯一遍更新就行了。
code:
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 1e5 + 5;
namespace G2D {
#define T double
#define eps 1e-8
struct P {T x, y;P(T _x, T _y):x(_x), y(_y){}P(){}};
inline int sg(T a) {return (a > -eps) - (a < eps);}
struct Line {
P s, t;double k, b;Line(){k = b = 0;}
Line(P _s, P _t):s(_s), t(_t){
if(s.x == t.x) k = 0, b = t.y > s.y ? t.y : s.y;
else k = (t.y - s.y) / (t.x - s.x), b = s.y - s.x * k;
}
double GetY(double x) {return k * x + b;}
};
}
G2D::Line lines[maxn];
namespace SGT {
using namespace G2D;
int t[maxn << 2], cnt;
bool cmp_line(int a, int b, int pos) {
double Ya = lines[a].GetY(pos), Yb = lines[b].GetY(pos);
return sg(Ya - Yb) ? Ya > Yb : a < b;
}
void add(int x, int l, int r, int rt) {
if(!t[rt]) t[rt] = x;
else {
double leftY = lines[x].GetY(l) - lines[t[rt]].GetY(l);
double rightY = lines[x].GetY(r) - lines[t[rt]].GetY(r);
int SL = sg(leftY), SR = sg(rightY);
if(SL == 1 && SR == 1) t[rt] = x;
else if(SL == 1 || SR == 1){
int mid = (l + r) >> 1;
if(SL > 0)
if(cmp_line(x, t[rt], mid)) add(t[rt], mid + 1, r, rt << 1 | 1), t[rt] = x;
else add(x, l, mid, rt << 1);
else
if(cmp_line(x, t[rt], mid)) add(t[rt], l, mid, rt << 1), t[rt] = x;
else add(x, mid + 1, r, rt << 1 | 1);
}
}
}
void insert(int x, int l, int r, int tl, int tr, int rt) {
if(l == tl && r == tr) {
add(x, l, r, rt);
return;
}
int mid = tl + tr >> 1;
if(r <= mid) insert(x, l, r, tl, mid, rt << 1);
else if(l > mid) insert(x, l, r, mid + 1, tr, rt << 1 | 1);
else insert(x, l, mid, tl, mid, rt << 1), insert(x, mid + 1, r, mid + 1, tr, rt << 1 | 1);
}
int query(int pos, int tl, int tr, int rt) {
if(tl == tr)
return t[rt];
int mid = tl + tr >> 1, ret;
if(pos <= mid) ret = query(pos, tl, mid, rt << 1);
else ret = query(pos, mid + 1, tr, rt << 1 | 1);
double Yret = lines[ret].GetY(pos), Yrt = lines[t[rt]].GetY(pos);
if(!sg(Yret - Yrt)) return ret < t[rt] ? ret : t[rt];
return sg(Yret - Yrt) == -1 ? t[rt] : ret;
}
}
int n, out, t, opt, a, b, c, d, cnt, p2 = 1e9, lastans = 0;
int main() {
scanf("%d", &t);
while(t--) {
scanf("%d", &opt);
if(opt == 1) {
scanf("%d%d%d%d", &a, &b, &c, &d);
a = ((a + lastans - 1) % 39989 + 1);
b = ((b + lastans - 1) % p2 + 1);
c = ((c + lastans - 1) % 39989 + 1);
d = ((d + lastans - 1) % p2 + 1);
if(a > c) swap(a, c), swap(b, d);
lines[++cnt] = G2D::Line(G2D::P(a, b), G2D::P(c, d));
SGT::insert(cnt, a, c, 1, 40000, 1);
} else {
scanf("%d", &a);
a = ((a + lastans - 1) % 39989 + 1), ++out;
printf("%d\n", lastans = SGT::query(a, 1, 40000, 1));
}
}
return 0;
}
rockdu
没有帐号? 立即注册