首页 > 其他分享 >李超树

李超树

时间:2023-09-29 23:45:08浏览次数:40  
标签:cnt return int y0 x0 x1 李超树

模板:P4097

  • 先考虑插入直线
  • 在每个节点存一个 \(f_i\) 表示一条直线。需要保证 \(u\) 及其祖先的 \(f\) 中有在 \(u\) 区间的中点处取得极值的那条直线。
  • 考虑更新。
  • 注意到一条直线完整覆盖一个区间时,它不需要下传,因为查询它的儿子时必然经过它本身,也就能统计这条直线的贡献。
  • 到达叶结点时直接比对并返回。
  • 否则更新中点极值并考虑在中点劣的那条直线能否更新其左右儿子。
  • 它们是直线,所以最多只能更新一边!
  • 于是复杂度是正确的。

感觉上,它在二分找相交点。它在巧妙地维护一个凸包,利用懒标记推平一段已经不优的区间,单侧递归保证了复杂度。这个是 \(O(\log n)\) 的。

进行线段树套李超树。(本质是把一个区间拆成 \(\log\) 个区间,本质不同的小区间只有 \(O(n)\) 个,长度之和 \(n \log n\)。)然后能做,多一只拆分的 \(\log\)。

感觉,很好理解啊!但是我理解了好久,不懂。好像是不理解正确性,画个图用凸包理解似乎就好不少了。当作线段树懒标记就算了。

#include <bits/stdc++.h>
const int M = 1e5 + 5, mod = 39989, mod2 = 1e9 + 1;
const double eps = 1e-9;
using pai = std::pair<double, int>;

struct line {
  double k, b;
  double w(int x) { return k * x + b; }
} l[M];

int cnt;

void add(int x0, int y0, int x1, int y1) {
  ++cnt;
  if (x0 == x1)
    l[cnt] = {0.0, 1.0 * std::max(y0, y1)};
  else
    l[cnt].k = 1.0 * (y1 - y0) / (x1 - x0), l[cnt].b = y0 - l[cnt].k * x0;
}

double w(int i, int x) { return l[i].k * x + l[i].b; }

int cmp(double x, double y) {
  if (x - y > eps) return 1;
  if (y - x > eps) return -1;
  return 0;
}

bool cmp(int i, int j, int x) {
  int c = cmp(w(i, x), w(j, x));
  if (c != 0) return c == 1 ? 1 : 0;
  return i < j;
}

pai mx(pai x, pai y) {
  int c = cmp(x.first, y.first);
  if (c != 0) return c == 1 ? x : y;
  return x.second < y.second ? x : y;
}

int s[M << 2];

void ins(int o, int l, int r, int v) { // 插入
  int &u = s[o], mid = l + r >> 1;
  if (!cmp(u, v, mid)) std::swap(u, v);
  if (!cmp(u, v, l)) ins(o << 1, l, mid, v);
  if (!cmp(u, v, r)) ins(o << 1 | 1, mid + 1, r, v);
}

void find(int o, int l, int r, int x, int y, int v) { // 找到所有子区间
  if (x <= l && r <= y) return ins(o, l, r, v), void();
  int mid = l + r >> 1;
  if (x <= mid) find(o << 1, l, mid, x, y, v);
  if (y > mid) find(o << 1 | 1, mid + 1, r, x, y, v);
}

pai qry(int o, int l, int r, int x) {
  if (l == r) return std::make_pair(w(s[o], x), s[o]);
  int mid = l + r >> 1;
  return mx(std::make_pair(w(s[o], x), s[o]), 
    x <= mid ? qry(o << 1, l, mid, x) : qry(o << 1 | 1, mid + 1, r, x));
}

int main() {
  int n, x0, y0, x1, y1, x, lans = 0;
  scanf("%d", &n);
  while (n--) {
    int op; scanf("%d", &op);
    if (op == 1) {
      scanf("%d %d %d %d", &x0, &y0, &x1, &y1);
      x0 = (x0 + lans - 1 + mod) % mod + 1;
      y0 = (y0 + lans - 1 + mod2) % mod2 + 1;
      x1 = (x1 + lans - 1 + mod) % mod + 1;
      y1 = (y1 + lans - 1 + mod2) % mod2 + 1;
      if (x0 > x1) std::swap(x0, x1), std::swap(y0, y1);
      add(x0, y0, x1, y1), find(1, 1, mod, x0, x1, cnt);
    } else {
      scanf("%d", &x);
      x = (x + lans - 1 + mod) % mod + 1;
      printf("%d\n", lans = qry(1, 1, mod, x).second);
    }
  }
  return 0;
}
/*
stupid mistakes:
  - l[i] = {x, l[i].k - x} ,算第二个参数时第一个参数还没计算完毕
*/

标签:cnt,return,int,y0,x0,x1,李超树
From: https://www.cnblogs.com/purplevine/p/li-chao-segtree.html

相关文章

  • 李超树
    去ZR的时候接触到的,然后来填坑。参考资料:李超树-XYini/bx。李超线段树初步-牛客竞赛浅谈李超线段树及其应用-tommymio应用李超线段树最经典的应用就是维护一个二维平面直角坐标系,支持动态插入线段或者直线,询问与直线\(x=x_0\)相交的已插入线段中交点\(y\)......
  • 李超树
    李超数支持动态插入线段/直线,查询单点极值。算法思想排除不可能成为最优解的,维护在当前区间能成为最优解的线段,即该线段在当前区间的某个取值上有最优解。查询的时间复杂度是\(O(\logn)\)的,修改时通过替换和下放,也能达到\(O(\logn)\)的复杂度,区间修改能达到\(O(\log^2n......
  • luogu P4069 [SDOI2016] 游戏 题解【李超树+树剖】
    目录题目描述解题思路code题目描述P4069[SDOI2016]游戏一棵树,树上有\(n\)个节点,最初每个节点上有\(1\)个数字:\(123456789123456789\)。有两种操作:\(\centerdot\)选择\(s,t\)两个节点,将路径上的每一个点都变多(\(1\)个变\(2\)个)数字:\(a\timesdis+b\),其中\(dis\)表示该节点......
  • 李超树浅谈
    李超树是一个可以多个分段一次函数,并取某个端点上所有一次分段函数的最值。基本李超树需要维护两个操作:在\([l,r]\)加入一条线段。询问在\(k\)处的最值。李超树中,每个节点我们存储的函数编号为在这个节点代表区间中点取得最值的函数编号。插入时,我们首先向线段树一样找......
  • 【题解】[HEOI2013]Segment(李超树)
    [HEOI2013]Segment题目分析:是李超线段树的板子题,在这里就稍微提一嘴李超线段树吧。其实李超线段树就是用来解决插入线段,查询\(x=k\)时纵坐标的最大值的。对于李超线......
  • CDQ 分治,李超树与斜率优化
    P4027,及一类类似问题:给定\(a_i,b_i,x_i,y_i\),对于每个\(i\)求出\(f_i=\max\limits_{j=1}^{i}\{a_ix_j+b_iy_j\}\)先说一下一类经典问题的做法:给定\(n\)个二......
  • 李超树(斜率优化无脑DS)
    如果我们需要一个数据结构来维护多个线段在一个点上的最值,那我们就可以使用李超树来完成这个事情。李超树的每个区间记录的是中点值最大的一条线段。如何做到呢?1、插入s......