BZOJ 1568
学习了一波李超线段树。
这个东西专门用来维护插入一条线段和区间/单点的最大/最小值。
插入的时候讨论:
1、如果当前结点上没有线段,那么直接插入。
2、如果当前结点上的线段一定比要插入的线段优/劣,那么直接覆盖或者返回。
3、如果当前结点上的线段和要插入的线段有交点,那么把优的部分比劣的部分多的线段放在当前结点上,然后把另一条线段下放给那个交点所在的儿子递归处理。
时间复杂度$O(nlogn)$,但是有返回这个操作看上去就很快……
Code:
#include <cstdio> #include <cstring> using namespace std; typedef double db; typedef long long ll; const int N = 5e4 + 5; int n = (int)5e4, qn; template <typename T> inline void chkMax(T &x, T y) { if(y > x) x = y; } namespace SegT { struct Node { db lb, lk; bool cov; } s[N << 2]; #define lc p << 1 #define rc p << 1 | 1 #define mid ((l + r) >> 1) #define lb s[p].lb #define lk s[p].lk #define cov s[p].cov void ins(int p, int l, int r, db b, db k) { if(!cov) { lb = b, lk = k, cov = 1; return; } db l1 = 1.0 * l * k + b, r1 = 1.0 * r * k + b; db l2 = 1.0 * l * lk + lb, r2 = 1.0 * r * lk + lb; if(l1 >= l2 && r1 >= r2) { lb = b, lk = k; return; } if(l1 <= l2 && r1 <= r2) return; db x = (b - lb) / (lk - k); if(l1 > l2) { if(x > mid) ins(rc, mid + 1, r, lb, lk), lb = b, lk = k; else ins(lc, l, mid, b, k); } else { /* if(x <= mid) ins(lc, l, mid, lb, lk), lb = b, lk = k; else ins(rc, mid + 1, r, lb, lk); */ if(x > mid) ins(rc, mid + 1, r, b, k); else ins(lc, l, mid, lb, lk), lb = b, lk = k; } } db query(int p, int l, int r, int x) { if(l == r) return lk * x + lb; db res = lk * x + lb; if(x <= mid) chkMax(res, query(lc, l, mid, x)); else chkMax(res, query(rc, mid + 1, r, x)); return res; } } using namespace SegT; int main() { scanf("%d", &qn); for(char op[12]; qn--; ) { scanf("%s", op); if(op[0] == 'P') { db b, k; scanf("%lf%lf", &b, &k); b -= k; ins(1, 1, n, b, k); } else { int x; scanf("%d", &x); db ans = query(1, 1, n, x); printf("%lld ", (ll) (ans / 100.0)); } } return 0; }