- 考虑以横坐标为下标维护线段树。
- 在每个结点维护一个标记,表示覆盖整个结点的最高线段,注意这个标记对整个区间都有作用,无需下传。
- 因为只有单点询问,所以可以不用维护区间的最高点之类的信息,操作可以简洁许多。我们只要保证每个横坐标受到影响的标记,覆盖到它的父亲结点或这个叶子结点本身即可。
- 修改的时候就只有可能出现两个标记对应的线段相交,我们需要根据交点的横坐标,往子区间修改标记,因为两个标记都会成为某些部分的最高点。
- 询问的时候只需要把从根结点到这个位置对应的叶子结点的标记取最高点即可。
- 这样我们修改的时候,每次访问到一个结点,最多只会往其中一个子节点递归修改。所以访问的最多结点数就是线段树的层数。
- 时间复杂度
O
(
n
log
2
n
)
O(n\log^2n)
O(nlog2n)。
- 这种线段树操作被称为李超线段树。
#include <bits/stdc++.h>
inline char nextChar()
{
static const int buffer_size = 2333333;
static char buffer[buffer_size];
static const char *tail = buffer + buffer_size;
static char *head = buffer + buffer_size;
if (head == tail)
{
fread(buffer, 1, buffer_size, stdin);
head = buffer;
}
return *head++;
}
inline void putChar(char ch)
{
static const int buffer_size = 2333333;
static char buffer[buffer_size];
static const char *tail = buffer + buffer_size;
static char *head = buffer;
if (ch == '\0')
fwrite(buffer, 1, head - buffer, stdout);
*head++ = ch;
if (head == tail)
fwrite(buffer, 1, buffer_size, stdout);
}
template <class T>
inline void putint(T x)
{
static char buf[15];
static char *tail = buf;
if (!x) return (void)(putChar('0'));
for (; x; x /= 10) *++tail = x % 10 + '0';
for (; tail != buf; --tail) putChar(*tail);
}
template <class T>
inline void read(T &x)
{
static char ch;
while (!isdigit(ch = nextChar()));
x = ch - '0';
while (isdigit(ch = nextChar()))
x = x * 10 + ch - '0';
}
template <class T>
inline void relax(T &x, const T &y)
{
if (x < y)
x = y;
}
const int MaxN = 1e5 + 5;
const int MaxS = MaxN << 2;
const int modx = 39989;
const int mody = 1e9;
const double eps = 1e-10;
int n, last_ans;
struct segment
{
int pos, ax, bx;
double ay, by;
segment(){}
segment(int p, int a, double b, int c, double d):
pos(p), ax(a), ay(b), bx(c), by(d) {}
inline double k()
{
return (ay - by) / (ax - bx);
}
inline double calc(int x)
{
return ax == bx ? std::max(ay, by) : ay + k() * (x - ax);
}
inline void l_relax(int x)
{
ay = calc(x), ax = x;
}
inline void r_relax(int x)
{
by = calc(x), bx = x;
}
}seg[MaxS];
#define lcc x << 1, l, mid
#define rcc x << 1 | 1, mid + 1, r
inline bool is_equal(double a, double b)
{
return fabs(a - b) <= eps;
}
inline bool cmp(segment x, segment y, int pos)
{
if (!x.pos || !y.pos) return x.pos;
double h1 = x.calc(pos);
double h2 = y.calc(pos);
if (!is_equal(h1, h2))
return h1 > h2;
else
return x.pos < y.pos;
}
inline void modify_node(int x, int l, int r, segment del)
{
if (del.ax < l) del.l_relax(l);
if (del.bx > r) del.r_relax(r);
int mid = l + r >> 1;
if (cmp(del, seg[x], mid))
std::swap(seg[x], del);
if (seg[x].ay > del.ay && seg[x].by > del.by)
return;
if (l == r) return;
if (seg[x].k() > del.k())
modify_node(lcc, del);
else
modify_node(rcc, del);
}
inline void modify(int x, int l, int r, segment del)
{
if (del.ax < l) del.l_relax(l);
if (del.bx > r) del.r_relax(r);
if (del.ax == l && r == del.bx)
return (void)(modify_node(x, l, r, del));
int mid = l + r >> 1;
if (del.ax <= mid)
modify(lcc, del);
if (del.bx > mid)
modify(rcc, del);
}
inline segment query(int x, int l, int r, int p)
{
if (l == r)
return seg[x];
int mid = l + r >> 1;
segment tmp = p <= mid ? query(lcc, p) : query(rcc, p);
return cmp(tmp, seg[x], p) ? tmp : seg[x];
}
int main()
{
read(n);
int tot = 0;
for (int i = 1; i <= n; ++i)
{
int opt, ax, ay, bx, by;
read(opt);
if (opt)
{
read(ax), read(ay), read(bx), read(by);
ax = (ax + last_ans - 1) % modx + 1;
bx = (bx + last_ans - 1) % modx + 1;
ay = (ay + last_ans - 1) % mody + 1;
by = (by + last_ans - 1) % mody + 1;
if (ax > bx)
std::swap(ax, bx), std::swap(ay, by);
if (ax == bx)
ay = by = std::max(ay, by);
modify(1, 1, modx, segment(++tot, ax, ay, bx, by));
}
else
{
read(ax);
ax = (ax + last_ans - 1) % modx + 1;
putint(last_ans = query(1, 1, modx, ax).pos);
putChar('\n');
}
}
putChar('\0');
return 0;
}