• 李超树


    李超树能解决的问题是:

    给一堆直线((y = kx + b),用((k, b))来表示),并且一边给,一边问x = pos的最大的 (y) 的值。


    做法

    维护一个线段树,每个节点维护一条当前区间内的优势直线,即从上往下看最长的直线 (其实是mid处最高的点所在直线), 且每条直线最多只被一个节点维护。

    要大力分类讨论(该点是否曾有直线,直线是否有交,哪条直线的上面部分更长)。注意 如果当前直线胜选,就要把原有直线扔下去,因为原有直线在下面的某些点可能更有优势。

    查询:从 根 到 代表该 (pos) 的叶子 的所有节点 所代表的直线在 (pos)(y) 值的最大值。

    (l_{pre}/r_{pre}) : 原有直线的左右端点。

    (l_{cur}/r_{cur}) : 正插入直线的左右端点。

    模板提交处: P4254 [JSOI2008]Blue Mary开公司 (记得一开始要 b -= k

    李超树加强版:“线段”版李超树

    然而最常见的还是“线段”版李超树,即给线段的左右端点,单点查询。把线段拆成许多完整覆盖线段树节点的小线段,然后再做。

    例题中要求最优线段编号(这同样也是常见的应用时的情况)。因此有如下代码,更加简便些:(提示:对 (mem) 的修改就相当于对 (id[cur]) 的修改。)(按斜率排序是个好东西)

    //毕竟每个节点存的是中间点的最高点,因此就直接比较两线段的中间点即可。
    void insert(int L, int R, int nwid, int &cur)
    void modify(int L, int R, int l, int r, int nwid, int &cur) {
    	if (!cur)	cur = ++ttot;
    	if (l <= L && R <= r) {
    		insert(L, R, nwid, cur);
    		return ;
    	}
    	int mid = (L + R) >> 1;
    	if (l <= mid)	modify(L, mid, l, r, nwid, ls[cur]);
    	if (r > mid)	modify(mid + 1, R, l, r, nwid, rs[cur]);
    }
    

    李超树二次加强版:P4069 [SDOI2016]游戏(查最低点)

    (dis[cur]) 当作横坐标,将 (cur) 上数字看作纵坐标,发现链上的数字与 (dis[cur]) 成一次函数关系(当然要拆成两种链)。再加之以树链剖分,操作就变成了一段段的线段((dfn) 数组上的),查询就是查询 一段段 区间内的最小值 的最小值。

    与上一道题不同,这回成了区间查询,且维护的是离散化后的线段树。

    也不难,维护 (mn) 表示当前节点子树内最高点。然后 (O(n)~ ~->O(logn)).

    至于离散化,不要忘记李超树其实就是在维护个凸包(类似半平面交),只不过由于横坐标只有有限种可能。因此维护每种横坐标的特殊值作为L和R的意义。注意,(mid) 还是 ((L + R) >> 1),只不过用的是mid的原数值 ((dis[ded[mid]]))

    注意!!!

    从根到叶子的所有线段都要算一下,包括拆成小线段节点之前的大节点,但是这些线段可能没有完全覆盖询问区间,要进行特判!!!!

    res = have_segments[cur] ? min(dis[ded[max(l, L)]] * k[cur] + b[cur], dis[ded[min(r, R)]] * k[cur] + b[cur]) : inf;
    

    不知道为什么会出现万分之一的错,不特判就是95WA。哪位大佬知道原因啊?(已经调到心态爆炸)

    应用

    (暂时鸽掉)

    CF932F Escape Through Leaf

    CF1303G Sum of Prefix Sums

    时间还是不够使,况且线段树等数据结构难调,斜率等计算几何更难调,等我有时间了最好还是多做做,锻炼码力

    2020.11.19 Update:

    一轮复习学了 zzz 大佬的写法,A 掉了 Escape Through Leaf(李超树合并),并没有上百行。感觉 zzz 大佬的写法好简便啊!现在感觉李超树也没那么难调了。于是删掉了之前的许多代码,毕竟那些太麻烦了。

    模板(调试用)

    来源:RPG游戏

    namespace LCT {
    	inline int calc(int x, int id) {
    		return b[id] * x + f[id] - (X(id) + Y(id)) * b[id];
    	}
    	inline bool cmp(int x, int lhs, int rhs) {
    		return calc(x, lhs) > calc(x, rhs);
    	}
    	int ls[NN], rs[NN], bst[NN], ttot;
    	void ins(int L, int R, int id, int &cur) {
    		if (!cur) {
    			cur = ++ttot;
    			bst[cur] = id;
    			return ;
    		}
    		bool tl = cmp(L, id, bst[cur]), tr = cmp(R, id, bst[cur]);
    		if (tl && tr)	return bst[cur] = id, void();
    		if (!tl && !tr)	return ;
    		int mid = (L + R) >> 1;
    		if (cmp(mid, id, bst[cur]))	swap(id, bst[cur]), tl = !tl, tr = !tr;
    		if (tl)	ins(L, mid, id, ls[cur]);
    		else	ins(mid + 1, R, id, rs[cur]);
    	}
    	int query(int L, int R, int x, int cur) {
    		if (!cur)	return -inf;
    		int res = calc(x, bst[cur]);
    		int mid = (L + R) >> 1;
    		if (x <= mid)	MAX(res, query(L, mid, x, ls[cur]));
    		else	MAX(res, query(mid + 1, R, x, rs[cur]));
    		return res;
    	}
    }
    
  • 相关阅读:
    葵花宝典,参考学习网站收藏
    安卓工具
    马帮
    C89:vs输出调试信息
    OSG:中级篇 拖拽器类
    OSG:幼儿园篇 第六章 碰撞检测类
    OSG:幼儿园篇 第三章 节点坐标变换类
    OSG:幼儿园篇 第五章 界面交互类
    C++11:智能指针
    OSG:幼儿园篇 第四章 节点回调类
  • 原文地址:https://www.cnblogs.com/JiaZP/p/14008009.html
Copyright © 2020-2023  润新知