线段树

大约是一颗李超线段树

孤街醉人 提交于 2019-12-04 10:46:35
%%% 李 超 由于观看 J oe 大神 颓废 学习发现了这种高级数据结构 才引来了这场灾难(我先改完T2才改完T1 说明书: 用途:维护最优一次函数 复杂度:$\Theta (n log_{2}n^{2})$ 食 使用指南: 我们先把线段树节点中存储的信息改为存储一条线 存储的线段不一定在区间的每个点上都最有,但是应该是在该区间最优的 有没有感觉很像 二维MLE线段树 当然也可能有这么两条线分别在同一区间不同位置最优 此时维护优的区间更长的 (以下假设维护最大值 区间修改:   我们考虑两根线段   首先我们要将线段在线段树上定位,此时会有$ log_{2}n$个区间需要修改   接下来我们只考虑一个区间了哈   经对两根线段的相对位置大力分类讨论得:   一共有四种情况   1.        线段$a$(蓝色)的斜率比当前最优线段$b$(或许是橙色)小   且两线段交点在中点右侧   那么显然对于当前区间以及左侧区间线段$a$更优,   递归到这里就可以停了(当前区间已经被修改后再去修改左侧区间毫无意义   但是对于右侧区间   线段$b$仍可能更优   此时应以线段$b$为参数递归修改右侧区间   2.       线段$a$(同上)的斜率比当前最优线段$b$(同上)小   且两线段交点在中点左侧   线段$a$仅在左侧区间可能更优   此时应以线段$a

Luogu P2073 送花

∥☆過路亽.° 提交于 2019-12-04 10:31:23
题目传送门 操作2和操作3 反着 写是什么鬼?反人类 权值线段树的模板题 然而AC后才发现,可以用 \(\tt{set}\) 水过…… 权值线段树类似于用线段树来实现平衡树的一些操作,代码实现还是比较方便的 #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define ls p<<1 #define rs p<<1|1 #define mid ((l+r)>>1) using namespace std; struct zzz{ int w,c; }tree[1000000<<2]; inline void up(int p){ //合并线段树的信息 tree[p].w=tree[ls].w+tree[rs].w; tree[p].c=0; if(tree[ls].w) tree[p].c+=tree[ls].c; if(tree[rs].w) tree[p].c+=tree[rs].c; } void build(int l,int r,int p){ //初始化线段树 if(l==r){ tree[p].c=l; return ; } build(l,mid,ls); build(mid+1,r,rs); } void add(int l,int r,int p,int k

Luogu P1558 色板游戏

假如想象 提交于 2019-12-04 10:31:02
题目传送门 此解法超级暴力。洛谷最优解倒数…… 因为为这道题只有 \(30\) 种颜色,所以我们可以用 30颗线段树 来分别维护每种颜色。 涂颜色就将对应颜色的线段树区间染色成 \(1\) ,其他的染成 \(0\) 。 统计颜色就把线段树都枚举一遍,统计在这个区间上有 \(1\) 的线段树数量 然而正解是压位线段树 思路好想,理论时间复杂度也能过,就是常数大,在洛谷不开 \(O_2\) 过不去…… #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define ls p<<1 #define rs p<<1|1 #define mid ((l+r)>>1) using namespace std; struct zzz{ //线段树 int tree[100010<<2],tag[100010<<2]; void build(int l,int r,int p){ if(l==r){ tree[p]=1; return ; } build(l,mid,ls); build(mid+1,r,rs); tree[p]=tree[ls]+tree[rs]; } inline void down(int l,int r,int p){ if(tag[p]==-1) return ; tree

线段树维护区间最大子段和

与世无争的帅哥 提交于 2019-12-04 10:28:55
线段树:我还是很强的 简略讲解 要用线段树维护区间,我们要明确: 线段树存什么东西 怎么合并 如果有区间修改,怎么打标记 对于区间最大子段和,我们可以记录四个值:以维护的区间左端点为起点的最大子段和,以维护的区间右端点为终点的最大子段和,在维护区间内的最大子段和 和维护区间所有元素的和 合并的话稍微麻烦一些,看代码吧: inline void up(int p){ tree[p].sum=tree[ls].sum+tree[rs].sum; //维护区间总和 tree[p].ll=max(tree[ls].ll,tree[ls].sum+tree[rs].ll); //左端点的最大子段和可能为左儿子的左端点最大子段和,也可能为左儿子区间和 和右儿子左端点最大子段和拼起来的最大子段和 tree[p].lr=max(tree[rs].lr,tree[rs].sum+tree[ls].lr); //右端点最大子段和同理 tree[p].lm=max(tree[ls].lr+tree[rs].ll,max(tree[ls].lm,tree[rs].lm)); //中间的最大子段和可能为左/右儿子中间的的最大子段和,也可能为左右儿子拼起来的和 } 然后我们的线段树那就可以维护最大子段和了。 例题 例 \(1\) : SPOJ 1043 GSS1 - Can you answer these

树链剖分不详细讲解

旧街凉风 提交于 2019-12-04 10:26:27
前置技能:线段树、DFS 当我第一次听到 “树链剖分” 这个算法的时候,感觉它一定很高大上。现在看来,它确实很高大上,不过也十分的 暴力 (个人认为,不喜勿喷) 基本概念  树链剖分,计算机术语,指一种对树进行划分的算法,它先通过轻重边剖分将树分为多条链,保证每个点属于且只属于一条链,然后再通过数据结构(树状数组、SBT、SPLAY、线段树等)来维护每一条链。  ————某度百科   百度百科对什么是树剖已经说的很明白了,接下来我们再了解一下其他的概念。 重儿子:对于每一个非叶子节点,它的儿子中子树节点最多的儿子 轻儿子:对于每一个非叶子节点,它的除重儿子以外的儿子 重边:父亲节点连向重儿子的边 轻边:父亲节点连向轻儿子的边 重链:由多条重边连成的一条树链 轻链:由多条轻边连成的一条树链 在这张图片中,带红点的就是轻儿子,其余为重儿子;加粗的边为重边,其余的为轻边; \(1 -> 14, \; 2 -> 11, \; 3 -> 7\) 的路径为重链,其余的为轻链。 前面某度已经说了,树链剖分要通过轻重边剖分将树分为多条链,那么它是怎么找出轻重边,又是怎么剖分的的呢?不要着急,我们接着讲 实现方法 先来说一说我们需要求哪些东西 变量 含义 \(f[i]\) 结点 \(i\) 的父亲 \(son[i]\) 结点 \(i\) 的重儿子(如果有 \(i\) 有两个及以上的重儿子

线段树合并

杀马特。学长 韩版系。学妹 提交于 2019-12-04 06:39:04
线段树合并 应用范围:将子树的信息合并给父亲节点,并且权值线段树的下标值域和节点数相近。 CF600E Lomsat gelral 题目链接 题意:一棵树有n个结点,每个结点都是一种颜色,每个颜色有一个编号,求树中每个子树的最多的颜色编号的和。 \(1 <= n<=1e5\) 解法:线段树合并,这个东西的时空复杂度都很玄学,姑且认为时间为 \(O(nlogn)\) ,空间为(常数 \(\times log_n\times n\) ),常数一般为 \(4-8\) 。 #include <cstdio> #include <algorithm> using namespace std; #define maxn 100100 #define ll long long int n; int fir[maxn], nxt[maxn * 2], vv[maxn * 2]; int tot = 0; void add(int u, int v) { nxt[++tot] = fir[u]; fir[u] = tot; vv[tot] = v; } int cnt = 0; int root[maxn], col[maxn]; int lz[maxn * 17 * 2], rz[maxn * 17 * 2], sum[maxn * 17 * 2]; ll ans[maxn * 17 * 2];

luogu_P3372 【模板】线段树 1(动态开点)

天涯浪子 提交于 2019-12-04 06:34:05
动态开点线段树 #include<iostream> #include<cstdio> #define ri register int #define u long long namespace opt { inline u in() { u x(0),f(1); char s(getchar()); while(s<'0'||s>'9') { if(s=='-') f=-1; s=getchar(); } while(s>='0'&&s<='9') { x=(x<<1)+(x<<3)+s-'0'; s=getchar(); } return x*f; } } using opt::in; #define NN 100005 namespace xds { struct node{ u l,r,sum,add; }a[NN<<1]; u num,root; void pushdown(const u &rt,const u &l,const u &r){ if(a[rt].add){ if(!a[rt].l) a[rt].l=++num; if(!a[rt].r) a[rt].r=++num; u mid((l+r)>>1); a[a[rt].l].add+=a[rt].add; a[a[rt].r].add+=a[rt].add; a[a[rt].l].sum+=a[rt]

魔法 [线段树优化DP]

半世苍凉 提交于 2019-12-04 06:24:29
也许更好的阅读体验 \(\mathcal{Description}\) 小 \(D\) 正在研究魔法。 小 \(D\) 得到了远古时期的魔法咒语 \(S\) ,这个咒语共有 \(n\) 个音节,每个音节都可以 抽象为一个小写英文字母。 但是很快小 \(D\) 发现这个咒语并不能直接说出——它具有一定的危险性。 小 \(D\) 进行了一些仔细的研究,很快发现危险来源于 \(m\) 个禁忌词 \(T_1 , T_2 , \ldots, T_m\) 。 小 \(D\) 发现,只要说出的咒语中,连续地包含了其中某个禁忌词,那么就会带来很 大的危险。换言之,对于任意 \(1 ≤ i ≤ m\) , \(T_i\) 都不能是最终说出的咒语 \(S'\) 的子串。 于是小 \(D\) 决定在原来的咒语 \(S\) 上做出一定的删减,使得它不再包含任何禁忌词。 小 \(D\) 发现如果他跳过咒语中第 \(i\) 个音节,那么咒语的威力会减少 \(a_i\) 。 小 \(D\) 想要知道,如何跳过音节可以得到一个安全的咒语,而威力的减少量最少。 值得一提的是,如果小 \(D\) 跳过了某个音节,那么与之相邻两个音节也不会变得连续。 但是小 \(D\) 并不会,请你帮帮他。 \(\mathcal{Solution}\) 先用所有的 \(T\) 对 \(S\) 做 \(KMP\) ,得到若干个区间

P3834 【模板】可持久化线段树 1(主席树)

二次信任 提交于 2019-12-04 05:29:31
P3834 【模板】可持久化线段树 1(主席树) Description 如题,给定N个整数构成的序列,将对于指定的闭区间查询其区间内的第K小值。 Input 第一行包含两个正整数N、M,分别表示序列的长度和查询的个数。 第二行包含N个整数,表示这个序列各项的数字。 接下来M行每行包含三个整数l,r,k , 表示查询区间[l,r]内的第k小值。 Outupt 输出包含k行,每行1个整数,依次表示每一次查询的结果 Sample Input 5 5 25957 6405 15770 26287 26465 2 2 1 3 4 1 4 5 1 1 2 2 4 4 1 Sample Output 6405 15770 26287 25957 26287 Data Size 对于100%的数据满足:1≤N,M≤2⋅10^5 对于数列中的所有数ai,均满足−10^9≤ai≤10^9 题解: 这题的详细说明可以跳转 主席树学习笔记 #include <iostream> #include <cstdio> #include <algorithm> #define N 200005 using namespace std; struct T {int l, r, sum;} t[N << 5]; int n, m, dex, cnt; int a[N], b[N], r[N]; int read(

[HDU4867]Xor (线段树分治+类数位dp)

烂漫一生 提交于 2019-12-04 04:48:40
[HDU4867]Xor (线段树分治+类数位dp) 提供一种 \((m+n) log a log m\) 带有常数约 \(\frac{1}{log n}\) 的算法 处理询问,将后来加入的数算进序列中,则每个数 \(a_i\) 都有一段出现的区间 \([L,R]\) 离线询问后,我们考虑用线段树分治将这些数加入到询问区间上 由于最多只有5000个询问,事实上这些数在线段树上覆盖的区间最多只有 \(10000logm\) 个,并且有着极其不满的常数(因为每个位置上的数都由多段区间组合而来,总长为 \(m\) ,或者你可以觉得我在放屁) 如果直接处理每个数的贡献,那么这个 \(dp\) 是 \(a*a\) 转移的 然而事实上我们存在一种 \(a*loga\) 的转移方法 对于一个数 \(x\) ,如果我们取 \(y \leq x\) 时,最高位为 \(0\) ,则后面的位均可以随便取 换句话说,对于每一个前 \(k\) 位相同的集合,它们都能够转移到它们之间的任何一个,可以直接累和 同样的,考虑在第 \(k\) 位出现一个 \(x\) 在该位为 \(1\) , \(y\) 为 \(0\) ,都具有类似的转移性质 最后写出来跟数位 \(dp\) 一个样子。。。 (真不行你可以试试某变换,但是我不会!) 这样的情况个数即这个数 \(1\) 位的个数,这样的个数期望情况下可以看做常数。。。