闲话 22.10.10

闲话

昨天晚上梦见机房里放《萤火虫从未来过》
然后切到《无梦之梦》
但是醒了还是新宝岛
果然想听歌了
哪位大佬能跟我说说饭的近况啊谢谢了

好今天一道李超树一道树上背包
我:你不是拿着系数吗怎么插出系数
lyin:你什么玩意你卷积不用点值乘
我:wssb

然而不想写拉插于是进行了一个指针释放内存的学习
旁边大佬用vector早早切掉了
我:这个玩意怎么接返回值啊

虽然最后还是切了
跑的很快(

所以我还是得给我前几天的鲜花引流
如果还没看的话可以去看看!

春風に靡く少女のように

なぜわたしたちは光がなかったの

どこまでも伸びる黒い影は

未来を見ている未来を見ている

关于李超树

李超线段树是一种用于维护平面直角坐标系内线段关系的数据结构。 - By JHSeng

[HEOI2013] Segment

你需要在线地支持 \(n\) 次两种操作:

  1. 加入一条定义域为 \([l, r]\) 的线段 \(l : y = kx + b\)
  2. 查询所有与 \(x = k\)\(k\) 为整数)相交线段中纵坐标最大的线段的编号。若有多条输出编号最小者。

\(n ,l,r,k\le 1e5\)

于是我们需要用到李超树来维护线段信息。

李超树是一种线段树,节点 \([l,r]\) 上维护的是该段区间上的优势线段,即在该段区间中点上取值最大的线段。询问时一层层地递归,取路径上优势线段的最大值为答案。
容易发现这样能够保证答案正确。因此李超树实际上是一棵标记永久化思想的线段树。

对于修改,我们查询到当前修改线段定义域完全包含的区间族,对于每个区间进行如下操作:

  1. 若该区间无优势线段,则更新该区间的优势线段为当前线段。
  2. 若该区间存在优势线段,分类讨论。
    1. 若该区间优势线段完全覆盖了当前线段,则直接返回。
    2. 若该区间优势线段被当前线段完全覆盖,则更新优势线段为当前线段并直接返回。
    3. 若该区间优势线段与当前线段在该区间内有交,则首先更新区间优势线段,并把更劣的线段递归地下传到子区间内。

由于该部分需要递归,因此更新一段区间的时间复杂度为 \(O(\log n)\)
若每次修改的是一段线段,则其共会修改 \(O(\log n)\) 个区间,因此维护线段的李超树更新总复杂度为 \(O(\log^2 n)\)
若每次修改的是一条直线,则其只会更新根节点,因此维护直线的李超树更新总复杂度为 \(O(\log n)\)

对于查询,按照正常线段树进行即可。复杂度 \(O(\log n)\)

code
#define rep(i,a,b) for (register int i = (a), i##_ = (b) + 1; i < i##_; ++i)
#define pre(i,a,b) for (register int i = (a), i##_ = (b) - 1; i > i##_; --i)
const int N = 1e6 + 10, mod1 = 39989, mod2 = 1e9;
const double eps = 1e-9;
using pdi = pair<double, int>;
int n, typ, t1, t2, t3, t4, lans;

int cnt;
struct line {
	double k, b;
} p[N];
void addl(int x_0, int y_0, int x_1, int y_1){
	++ cnt;
	if (x_0 == x_1) p[cnt] = {0, max(y_0, y_1)};
	else p[cnt].k = 1.0 * (y_1 - y_0) / (x_1 - x_0), p[cnt].b = y_0 - p[cnt].k * x_0;
}

double calc(int id, int x) {
	return p[id].k * x + p[id].b;
}
int cmp(double x, double y){
	if (x - y > eps) return 1;
	if (y - x > eps) return -1;
	return 0;
}

struct SegmentNeon {
	int id;
	#define ls (p << 1)
	#define rs (p << 1 | 1)
	#define id(p) seg[p].id
} seg[N<<2];

void __upd(int p, int l, int r, int idn) {
	int mid = l + r >> 1;
	if (cmp(calc(idn, mid), calc(id(p), mid)) == 1) swap(idn, id(p));
	int bl = cmp(calc(idn, l), calc(id(p), l)), br = cmp(calc(idn, r), calc(id(p), r));
	if (bl == 1 or (!bl and idn < id(p))) __upd(ls, l, mid, idn);
	if (br == 1 or (!br and idn < id(p))) __upd(rs, mid+1, r, idn);
}

void upd(int p, int l, int r, int L, int R, int idn) {
	if (L <= l and r <= R) {
		__upd(p, l, r, idn);
		return;
	} int mid = l + r >> 1;
	if (L <= mid) upd(ls, l, mid, L, R, idn);
	if (mid < R) upd(rs, mid+1, r, L, R, idn);
}

pdi ckmax(pdi x, pdi y) {
	if (cmp(x.first, y.first) == -1) return y;
	else if (cmp(x.first, y.first) == 1) return x;
	else return x.second < y.second ? x : y;
} template <typename ...Args> pdi ckmax(pdi x, Args... y) { return ckmax(x, ckmax(y...)); }

pdi qry(int p, int l, int r, int x) {
	if (r < x or x < l) return {0, 0};
	int mid = l + r >> 1;
	double res = calc(id(p), x);
	if(l == r) return {res, id(p)};
	else return ckmax( {res, id(p)}, qry(ls, l, mid, x), qry(rs, mid+1, r, x) );
}

动态开点

平凡。

合并

两个点的两根线段取更优的一根作新点的优势线段,另一根作为新线段插入子节点中。

然后就可以搞一系列斜率优化问题了。

例题咕着,等以后水鲜花用。

posted @ 2022-10-10 19:21  joke3579  阅读(68)  评论(8编辑  收藏  举报