• 李超线段树


    李超线段树

    也可以叫李超树,用于维护线段、直线,并求出最值,基于标记永久化

    通常李超树的题 x" role="presentation" style="position: relative;">x 的范围可以接受

    • 标记永久化:省去 pushdown ,修改时一路更改被影响到的点,

      询问时一路累加路上的标记,在一些地方也能省去 pushup

    维护直线

    [JSOI2008]Blue Mary开公司

    将这题转换为两个操作:

    • 加入一条直线
    • 查询所有直线在 x=T" role="presentation" style="position: relative;">x=T 时取到的最大值。

    tg" role="presentation" style="position: relative;">tg 维护区间内在 x=mid" role="presentation" style="position: relative;">x=midy" role="presentation" style="position: relative;">y 值最大的函数编号,每次插入只需分类讨论。这里有两种方法

    假设插入函数为 x" role="presentation" style="position: relative;">x ,当前区间 tg" role="presentation" style="position: relative;">tg 对应函数为 y" role="presentation" style="position: relative;">y ,当前区间是 [l,r]" role="presentation" style="position: relative;">[l,r] ,中点是 mid" role="presentation" style="position: relative;">mid

    sol 1

    1. x" role="presentation" style="position: relative;">x 的斜率大于 y" role="presentation" style="position: relative;">y

      • x" role="presentation" style="position: relative;">xmid" role="presentation" style="position: relative;">mid 处的取值大于 y" role="presentation" style="position: relative;">y 的。 y" role="presentation" style="position: relative;">y 在右半区间一定不会优于 x" role="presentation" style="position: relative;">x

        y" role="presentation" style="position: relative;">y 去更新左区间(注意是用 y" role="presentation" style="position: relative;">y 更新)。 tg" role="presentation" style="position: relative;">tg 更新为 x" role="presentation" style="position: relative;">x 的编号

      • 否则。 x" role="presentation" style="position: relative;">x 在左半区间一定不会优于 y" role="presentation" style="position: relative;">y ,用 x" role="presentation" style="position: relative;">x 去更新右半区间。

    2. 否则

      • x" role="presentation" style="position: relative;">xmid" role="presentation" style="position: relative;">mid 处的取值大于 y" role="presentation" style="position: relative;">y 的。y" role="presentation" style="position: relative;">y 在左半区间一定不会优于 x" role="presentation" style="position: relative;">x

        y" role="presentation" style="position: relative;">y 去更新右半区间。tg" role="presentation" style="position: relative;">tg 更新为 x" role="presentation" style="position: relative;">x 的编号

      • 否则。x" role="presentation" style="position: relative;">x 在右半区间一定不会优于 y" role="presentation" style="position: relative;">y ,用 x" role="presentation" style="position: relative;">x 去更新左半区间

    这种分类可得:每一次只会选择一种情况。最终保证单次修改复杂度为 O(logn)" role="presentation" style="position: relative;">O(logn)

    sol 2

    1. x" role="presentation" style="position: relative;">xl,r" role="presentation" style="position: relative;">l,r 两处的取值都大于 y" role="presentation" style="position: relative;">y 的,直接更新 tg" role="presentation" style="position: relative;">tg ,返回

    2. 比较 x,y" role="presentation" style="position: relative;">x,ymid" role="presentation" style="position: relative;">mid 的取值,更新 tg" role="presentation" style="position: relative;">tg ,以及用于更新的线段。

      • 实现比较巧妙:将 y" role="presentation" style="position: relative;">y 定义为引用变量,如果 x" role="presentation" style="position: relative;">xmid" role="presentation" style="position: relative;">mid 处取值大于 y" role="presentation" style="position: relative;">y 的就 swap(x, y)

        得到 y" role="presentation" style="position: relative;">y 是较优的那个, x" role="presentation" style="position: relative;">x 用于更新。

    3. x" role="presentation" style="position: relative;">xl" role="presentation" style="position: relative;">l 处取值大于 y" role="presentation" style="position: relative;">y 的, x" role="presentation" style="position: relative;">x 可能在左区间更优,更新左区间

    4. x" role="presentation" style="position: relative;">xr" role="presentation" style="position: relative;">r 处取值大于 y" role="presentation" style="position: relative;">y 的, x" role="presentation" style="position: relative;">x 可能在右区间更优,更新右区间。

    同 sol1 的分析,复杂度为 O(logn)" role="presentation" style="position: relative;">O(logn)

    code

    最终 sol2 更加简洁,常数更小,此处用 sol2 实现

    查询类似单点查询,将路上所有直线在 x=T" role="presentation" style="position: relative;">x=T 取到的值都取最值

    1. #include
    2. using namespace std;
    3. typedef unsigned long long uLL;
    4. typedef long double LD;
    5. typedef long long LL;
    6. typedef double db;
    7. const int N = 2e5 + 5;
    8. int n, cnt, tg[N << 2];
    9. db ans;
    10. char op[15];
    11. struct L {
    12. db k, b;
    13. } a[N];
    14. inline db F(int i, int x) { return a[i].k * (x - 1) + a[i].b; }
    15. #define ls (rt << 1)
    16. #define rs (rt << 1 | 1)
    17. void mdy(int x, int l, int r, int rt) {
    18. register int mid = l + r >> 1, &y = tg[rt];
    19. if (F(x, l) >= F(y, l) && F(x, r) >= F(y, r)) { tg[rt] = x; return; }
    20. if (l == r) return;
    21. if (F(x, mid) > F(y, mid)) swap(x, y);
    22. if (F(x, l) > F(y, l)) mdy(x, l, mid, ls);
    23. if (F(x, r) > F(y, r)) mdy(x, mid + 1, r, rs);
    24. }
    25. void ask(int p, int l, int r, int rt) {
    26. ans = max(ans, F(tg[rt], p));
    27. if (l == r) return;
    28. register int mid = l + r >> 1;
    29. if (p <= mid) ask(p, l, mid, ls);
    30. else ask(p, mid + 1, r, rs);
    31. }
    32. #undef ls
    33. #undef rs
    34. int main() {
    35. scanf("%d", &n);
    36. for (int Ti = n; Ti--; ) {
    37. scanf("%s", op);
    38. if (op[0] == 'Q') {
    39. register int t;
    40. scanf("%d", &t);
    41. ans = 0;
    42. ask(t, 1, 200000, 1);
    43. printf("%d\n", (int)ans / 100);
    44. } else {
    45. ++cnt;
    46. scanf("%lf%lf", &a[cnt].b, &a[cnt].k);
    47. mdy(cnt, 1, 200000, 1);
    48. }
    49. }
    50. }

    维护线段

    [HEOI2013]Segment

    相当于多了一个定义域 x[x0,x1]" role="presentation" style="position: relative;">x[x0,x1] ,类似区间修改

    先找到线段树中被修改区间包含的区间,再用维护直线的方法下传、更新 tg" role="presentation" style="position: relative;">tg

    复杂度分析:首先将查询分到 logn" role="presentation" style="position: relative;">logn 个线段树上区间,对于这些区间又要 O(logn)" role="presentation" style="position: relative;">O(logn) 的时间下传

    单次修改 O(nlog2n)" role="presentation" style="position: relative;">O(nlog2n)

    1. #include
    2. using namespace std;
    3. typedef unsigned long long uLL;
    4. typedef long double LD;
    5. typedef long long LL;
    6. typedef double db;
    7. const int N = 1e5 + 5, M = 39989, P = 1e9;
    8. int n, cnt, tg[N << 2], ans;
    9. db mxx;
    10. struct L { db k, b; } p[N];
    11. inline db F(int i, int x) { return p[i].k * x + p[i].b; }
    12. #define ls (rt << 1)
    13. #define rs (rt << 1 | 1)
    14. void mdy(int ql, int qr, int x, int l, int r, int rt) {
    15. if (ql > r || l > qr) return;
    16. register int mid = l + r >> 1;
    17. if (ql <= l && r <= qr) {
    18. register int &y = tg[rt];
    19. if (F(x, l) > F(y, l) && F(x, r) > F(y, r)) {
    20. tg[rt] = x;
    21. return;
    22. }
    23. if (l == r) return;
    24. if (F(x, mid) > F(y, mid)) swap(x, y);
    25. if (F(x, l) > F(y, l)) mdy(ql, qr, x, l, mid, ls);
    26. if (F(x, r) > F(y, r)) mdy(ql, qr, x, mid + 1, r, rs);
    27. return;
    28. }
    29. mdy(ql, qr, x, l, mid, ls);
    30. mdy(ql, qr, x, mid + 1, r, rs);
    31. }
    32. inline void cmx(int i, int x) {
    33. register db v = F(i, x);
    34. if (v > mxx || (v == mxx && i < ans)) ans = i, mxx = v;
    35. }
    36. void ask(int x, int l, int r, int rt) {
    37. cmx(tg[rt], x);
    38. if (l == r) return;
    39. register int mid = l + r >> 1;
    40. if (x <= mid) ask(x, l, mid, ls);
    41. else ask(x, mid + 1, r, rs);
    42. }
    43. #undef ls
    44. #undef rs
    45. int main() {
    46. scanf("%d", &n);
    47. for (int Ti = n, ls = 0, op; Ti; Ti--) {
    48. scanf("%d", &op);
    49. if (!op) {
    50. register int t;
    51. scanf("%d", &t);
    52. t = (t + ls - 1) % M + 1;
    53. mxx = 0;
    54. ans = 0;
    55. ask(t, 1, M, 1);
    56. printf("%d\n", ls = ans);
    57. } else {
    58. register int xx, yy, x2, y2;
    59. scanf("%d%d%d%d", &xx, &yy, &x2, &y2);
    60. xx = (xx + ls - 1) % M + 1;
    61. x2 = (x2 + ls - 1) % M + 1;
    62. yy = (yy + ls - 1) % P + 1;
    63. y2 = (y2 + ls - 1) % P + 1;
    64. if (xx > x2) swap(xx, x2), swap(yy, y2);
    65. ++cnt;
    66. if (xx == x2) p[cnt].k = 0, p[cnt].b = max(yy, y2);
    67. else p[cnt].k = 1.0 * (yy - y2) / (1.0 * xx - x2), p[cnt].b = 1.0 * yy - p[cnt].k * xx;
    68. mdy(xx, x2, cnt, 1, M, 1);
    69. }
    70. }
    71. }

    强行结合

    [SDOI2016]游戏

    查询上链?直接树剖。

    同时根据 lca" role="presentation" style="position: relative;">lca 将询问分成两段、两条直线。

    1. #include
    2. using namespace std;
    3. typedef unsigned long long uLL;
    4. typedef long double LD;
    5. typedef long long LL;
    6. typedef double db;
    7. char buf[100000], *S = buf, *T = buf;
    8. inline char G() { return S == T && (T = (S = buf) + fread(buf, 1, 100000, stdin), S == T) ? EOF : *S++; }
    9. inline int Rd() {
    10. register int x = 0, C = G(), f = 1;
    11. for (; C < '0' || C > '9'; C = G()) f &= C ^ '-';
    12. for (; C > '/' && C < ':'; C = G()) x = (x << 1) + (x << 3) + (C ^ 48);
    13. return f ? x : -x;
    14. }
    15. const LL INF = 123456789123456789ll;
    16. const int N = 1e5 + 5;
    17. int n, Ti, lst[N], Ecnt, clk, tot;
    18. int dfn[N], fa[N], dep[N], sz[N], son[N], top[N], rk[N];
    19. int tg[N << 2];
    20. LL dis[N], mn[N << 2], ans;
    21. struct L { LL k, b; } p[N << 1];
    22. struct Ed { int to, nxt; LL qz; } e[N << 1];
    23. inline LL F(int i, int x) { return p[i].k * dis[rk[x]] + p[i].b; }
    24. inline void Ae(int fr, int go, int vl) { e[++Ecnt] = (Ed){ go, lst[fr], 1ll * vl }, lst[fr] = Ecnt; }
    25. void dfs1(int u, int ff) {
    26. fa[u] = ff, dep[u] = dep[ff] + (sz[u] = 1);
    27. for (int i = lst[u], v; i; i = e[i].nxt)
    28. if ((v = e[i].to) ^ ff) {
    29. dis[v] = dis[u] + e[i].qz;
    30. dfs1(v, u), sz[u] += sz[v];
    31. if (sz[v] > sz[son[u]]) son[u] = v;
    32. }
    33. }
    34. void dfs2(int u, int nw) {
    35. dfn[u] = ++clk, rk[clk] = u, top[u] = nw;
    36. if (son[u]) dfs2(son[u], nw);
    37. for (int i = lst[u], v; i; i = e[i].nxt)
    38. if ((v = e[i].to) ^ fa[u] && v ^ son[u]) dfs2(v, v);
    39. }
    40. #define ls (rt << 1)
    41. #define rs (rt << 1 | 1)
    42. void bui(int l, int r, int rt) {
    43. mn[rt] = INF, tg[rt] = 1;
    44. if (l == r) return;
    45. register int mid = l + r >> 1;
    46. bui(l, mid, ls), bui(mid + 1, r, rs);
    47. }
    48. inline void Up(int rt) { mn[rt] = min(mn[rt], min(mn[ls], mn[rs])); }
    49. void mdy(int ql, int qr, int x, int l, int r, int rt) {
    50. if (ql > r || l > qr) return;
    51. register int mid = l + r >> 1;
    52. if (ql <= l && r <= qr) {
    53. mn[rt] = min(mn[rt], min(F(x, l), F(x, r)));
    54. register int &y = tg[rt];
    55. if (F(x, l) <= F(y, l) && F(x, r) <= F(y, r)) { tg[rt] = x; return; }
    56. if (l == r) return;
    57. if (F(x, mid) < F(y, mid)) swap(x, y);
    58. if (F(x, l) < F(y, l)) mdy(ql, qr, x, l, mid, ls);
    59. if (F(x, r) < F(y, r)) mdy(ql, qr, x, mid + 1, r, rs);
    60. return Up(rt);
    61. }
    62. mdy(ql, qr, x, l, mid, ls), mdy(ql, qr, x, mid + 1, r, rs), Up(rt);
    63. }
    64. void ask(int ql, int qr, int l, int r, int rt) {
    65. if (ql > r || l > qr) return;
    66. if (ql <= l && r <= qr) { ans = min(ans, mn[rt]); return; }
    67. register int mid = l + r >> 1;
    68. ans = min(ans, min(F(tg[rt], max(l, ql)), F(tg[rt], min(r, qr))));
    69. ask(ql, qr, l, mid, ls), ask(ql, qr, mid + 1, r, rs);
    70. }
    71. #undef ls
    72. #undef rs
    73. inline int LCA(int x, int y) {
    74. for (; top[x] ^ top[y]; x = fa[top[x]])
    75. if (dep[top[x]] < dep[top[y]]) x ^= y ^= x ^= y;
    76. return dep[x] < dep[y] ? x : y;
    77. }
    78. inline void change(int x, int y, int z) {
    79. for (; top[x] ^ top[y]; x = fa[top[x]])
    80. mdy(dfn[top[x]], dfn[x], z, 1, n, 1);
    81. mdy(dfn[y], dfn[x], z, 1, n, 1);
    82. }
    83. inline LL query(int x, int y) {
    84. for (ans = INF; top[x] ^ top[y]; x = fa[top[x]]) {
    85. if (dep[top[x]] < dep[top[y]]) x ^= y ^= x ^= y;
    86. ask(dfn[top[x]], dfn[x], 1, n, 1);
    87. }
    88. if (dep[x] < dep[y]) x ^= y ^= x ^= y;
    89. ask(dfn[y], dfn[x], 1, n, 1);
    90. return ans;
    91. }
    92. int main() {
    93. n = Rd(), Ti = Rd();
    94. for (int i = 1, u, v, w; i < n; i++) {
    95. u = Rd(), v = Rd(), w = Rd();
    96. Ae(u, v, w), Ae(v, u, w);
    97. }
    98. dfs1(1, 0), dfs2(1, 1), bui(1, n, 1);
    99. p[++tot] = (L){ 0, INF };
    100. for (int op, x, y, a, b, l; Ti--; ) {
    101. op = Rd(), x = Rd(), y = Rd();
    102. if (op == 1) {
    103. a = Rd(), b = Rd(), l = LCA(x, y);
    104. p[++tot] = (L){ -1ll * a, dis[x] * a + b };
    105. change(x, l, tot);
    106. p[++tot] = (L){ 1ll * a, (dis[x] - dis[l] * 2) * a + b };
    107. change(y, l, tot);
    108. } else printf("%lld\n", query(x, y));
    109. }
    110. }

    维护凸包 & 优化 dp

    咕了,不会,去学 dp 了。。。

  • 相关阅读:
    我们为什么需要 DAO 操作系统?
    Android 内存缓存框架 LruCache 的实现原理,手写试试?
    ssh指定的密钥协商方式以及Ansible的hosts文件修改密钥协商方式
    微服务学习--1入门
    与CSDN相识的第五周年
    JavaScript执行机制
    HTML5期末大作业:基于HTML+CSS+JavaScript仿蘑菇街购物商城设计毕业论文源码
    http缓存
    最近少更新的原因
    洛谷刷题入门篇:顺序结构
  • 原文地址:https://blog.csdn.net/KonjakuLAF/article/details/126314128