线段树

2019.7.15-7.20暑假集训总结

匿名 (未验证) 提交于 2019-12-02 23:49:02
// 7.18前三天的等我周日再继续复习总结orz _(:з」∠)_每一天都是充实的欧 7.15:差分与前缀和 7.16: 倍增与ST表 7.17:并查集进阶 7.18:树状数组与线段树 树状数组: 1 .引入lowbit(x) int lowbit(int i) { } -11=(0100+1)=0101 &1011 树状数组 c[x],比如c[4], 4的二进制是(100),100刚好是3位数, 所以这个树状数组到c[4]为止 高度 是3。 除了树根,c[x]的 父节点为 c[x+lowbit(x)] 能用树状数组尽量用树状数组,快,省空间。( 一般用来单点更新,区间求和,维护最大值二不能维护最小值。 但是 求区间最大值的时候复杂度比较高 容易TLE,这个时候还是需要用到 线段树 ) “细细观察二进制 树状数组追其根本就是二进制的应用” 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int M =100000+100; 4 int n,t; 5 int a[M],C[M]; 6 int lowbit(int x) 7 { 8 return x&(-x); 9 } 10 int sum(int x) 11 { 12 int ret=0; 13 while(x) 14 { 15 ret+=C[x]; 16 x-

线段树的懒惰标记盲区记录

匿名 (未验证) 提交于 2019-12-02 23:49:02
最近又写上了线段树的懒惰标记延迟更新,发现以前对线段树的理解存在盲区,以至于,按自己的理解写出的代码存在bug,只能过过样例。现在将这个盲区清理掉。对于线段树延迟标记的含义是:在找到一区间要更新时,先将那一点区间更新,并标记,此标记可以理解成累计标记,及此点之前经历过标记,则此次标记则为两次标记的累加,不能累加的标记则不能用,比如加法乘法或异或与等可以累加,但其中两个不同的则不能累加。另外,pushdown()函数的作用便是把标记往下推,并使上一层的标记清除。这个函数是在更新以及访问的时候用的,就是当访问到此点时,发现还没有找到所需的范围,即需要的范围更小,则将大范围的标记往下推,直到找到对应的范围。至于更新时,找到此范围,在update里,直接累计标记。 以下是题目链接:https://ac.nowcoder.com/acm/contest/275/C?tdsourcetag=s_pcqq_aiomsg 链接: https://ac.nowcoder.com/acm/contest/275/C?tdsourcetag=s_pcqq_aiomsg 来源:牛客网 题目描述 在心理疏导室中有一种奇特的疏导工具,叫做红球。红球被提前分为了许多正方形小方格。 每当有人来找ATB做心理疏导时,ATB就会让他去先玩红球,然后通过红球小格方的高度来判断一个人的压力程度的高低 具体地讲

回滚线段树+bitset优化01背包――cf981E

匿名 (未验证) 提交于 2019-12-02 23:48:02
/*首先考虑如何计算一个点的可能凑出的值,这就是一个01可行性背包问题那么再拓展到一段区间[1..n]的点上,每个query都可以看做是一段区间上的点[l,r]加上一个体积为x的物品,转换到01背包上就是进行一次更新那么用线段树来维护每个query的区间更新 每个位置(区间)维护一个bitset,每次加入a都进行一次01背包 用线段树来维护区间的bitset,表示一段区间能组成的值 但是没法用lazy,每次区间更新只能停留在一段区间可以把每次停留在区间的数a用vector保存下来,当进行完所有的更新时,从线段树叶子结点开始向上回滚每个区间的可行性是左儿子的可行性|右儿子的可行性|vector里存的每个a提供的可行性贡献 */ #include<bits/stdc++.h> using namespace std; int n,m; #define maxn 10005 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 struct Node{int l,r,x;}p[maxn]; bitset<10005>seg[maxn<<2]; vector<int>v[maxn<<2]; void build(int l,int r,int rt){ seg[rt][0]=1; if(l==r)return; int m=l+r>>1;

HDU - 5390 tree 线段树套字典树 (看题解)

匿名 (未验证) 提交于 2019-12-02 23:48:02
HDU - 5390 看到的第一感觉就是树链剖分 + 线段树套字典树, 感觉复杂度不太对。 其实这种路径其实很特殊, 一个点改变只会影响它儿子到根的路径, 并且这种求最优值问题可以叠加。 所以我们修改的时候对对应dfs序打标记, 询问的时候在线段树上从上往下对每个对应区间求个最优值。 这样还会被卡MLE。。 需要分层优化一下。 #pragma GCC optimize(2) #pragma GCC optimize(3) #include<bits/stdc++.h> #define LL long long #define LD long double #define ull unsigned long long #define fi first #define se second #define mk make_pair #define PLL pair<LL, LL> #define PLI pair<LL, int> #define PII pair<int, int> #define SZ(x) ((int)x.size()) #define ALL(x) (x).begin(), (x).end() #define fio ios::sync_with_stdio(false); cin.tie(0); using namespace std; const int N

洛谷 P3373 【模板】线段树 2

匿名 (未验证) 提交于 2019-12-02 23:48:02
Ŀ¼ ˼· $Code$ 戳 ˼· 乘法优先(说了和没说一样,qwq) $Code$ #include<iostream> #include<cstring> #include<cstdio> #include<string> #include<algorithm> #define LL long long #define MAXN 100001 using namespace std; LL n,m,mod; struct node{ LL l,r,w,mul,add; }tree[MAXN*4]; inline LL read(){ LL x=0;bool f=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=!f;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return f?-x:x; } void pushdown(LL now){ if(tree[now].l==tree[now].r) return; tree[now<<1].w=(tree[now<<1].w*tree[now].mul+tree[now].add*(tree[now<<1].r-tree[now<<1].l+1))%mod; tree[now<<1|1].w=

(线段树操作)

匿名 (未验证) 提交于 2019-12-02 23:47:01
https://codeforces.com/contest/1187/problem/D 题意:选取a序列的一段【l,r】将选中的区间按非降排序。问能否经过若干次操作后形成b序列; 分析:b序列的数的总类及各个类的数目一定要与a序列相同;    对b的每个位置的值,找到与之相同的a序列中还没有被用的位置nowpos,然后判断1到nowpos的最小值是否等于b序列的当前值,再将该位置修改为无效值; #include<bits/stdc++.h> using namespace std; const int M=3e5+5; const int inf=0x3f3f3f3f; int tree[M<<2],a[M],b[M],aa[M],bb[M],ll[M]; vector<int>pos[M]; void up(int root){ tree[root]=min(tree[root<<1],tree[root<<1|1]); } void build(int root,int l,int r){ if(l==r){ tree[root]=a[l]; return ; } int midd=(l+r)>>1; build(root<<1,l,midd); build(root<<1|1,midd+1,r); up(root); } int query(int L,int R,int

Subarray Sorting (线段树)

匿名 (未验证) 提交于 2019-12-02 23:47:01
题意:给你两个长度为 n 的序列 a 和 b , 可以对 a 进行 操作: 选择一段区间[ l, r ] ,使得序列a 在这段区间里 按升序排序。 可以对a 进行任意多次操作,问 a是否有可能变成b序列。 解: 首先,枚举b序列,然后在a中,找这个元素在a出现的位置(如果是第一次出现的,就找第一次出现的那个位置,第二次出现就找第二次出现的) pos ,然后查询 0 ~ pos 的最小值 (线段树),若 最小值是 b对应的那个元素,就满足,否则,就不满足。 然后若满足,需要更新线段树,把查询的元素更新为INF,不然会影响后面查询。 /// 试公式的 勿cheat #include <iostream> #include <cstdio> #include <fstream> #include <algorithm> #include <cmath> #include <deque> #include <vector> #include <queue> #include <string> #include <cstring> #include <map> #include <stack> #include <set> #define LL long long #define ULL unsigned long long #define re register #define rep(i

NOI2018 你的名字――SAM+线段树合并

匿名 (未验证) 提交于 2019-12-02 23:43:01
题目链接在这里 洛谷 / LOJ 有一个串 \(S\) ,每次询问给你一个串 \(T\) ,两个数 \(L\) 和 \(R\) ,问你 \(T\) 有多少个本质不同的子串不是 \(S[L,R]\) 的子串 SOLUTION 如果你做过 生成魔咒 和 CF1037H ,就会做这道题了 有两个坑点: 1.线段树合并时必须每次都新建结点,因为两颗树都得保留 2.每次失配时必须先尝试减小已经匹配的长度,无法继续减少时再跳 \(suflink\) 我的大常数代码 #include <algorithm> #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <random> #include <string> #include <vector> #include <cmath> #include <ctime> #include <queue> #include <map> #include <set> #define IINF 0x3f3f3f3f3f3f3f3fLL #define u64 unsigned long long #define pii pair<int, int> #define mii map<int, int> #define u32 unsigned

[教程] 关于一种比较特别的线段树写法

匿名 (未验证) 提交于 2019-12-02 23:43:01
这篇NOIP水平的blog主要是为了防止我AFO后写法失传而写的(大雾) 博主平常写线段树的时候经常用一种结构体飞指针的写法, 这种写法具有若干优势: 条理清晰不易写挂, 且不需要借助宏定义就可以实现这一点 可以在很小的修改的基础上实现线段树的各种灵活运用, 比如: 可持久化 动态开点 线段树合并 出错会报RE方便用gdb一类工具快速定位错误(平衡树也可以用类似写法, 一秒定位板子错误) 而且将线段树函数中相对比较丑陋的部分参数隐式传入, 所以 (可能) 看上去比较漂亮一些 在使用内存池而不是动态内存的情况下一般比普通数组写法效率要高 原生一体化, 在数据结构之间嵌套时可以直接套用而不必进行各种兼容性修改 接口作为成员函数出现, 不会出现标识符冲突(重名)的情况 下面就以线段树最基础的实现例子: 在 \(O(n+q\log n)\) 的时间复杂度内对长度为 \(n\) 的序列进行 \(q\) 次区间加法区间求和为例来介绍一下这种写法. 对某道题目的完整实现或者其他的例子可以参考我的其他博文中的附带代码或者直接查询我在UOJ/LOJ的提交记录. (可能我当前的写法并没有做到用指针+结构体所能做到的最优美的程度而且没有做严格封装, 求dalao轻喷) 注意这篇文章的重点是写法而不是线段树这个知识点qwq... 前置技能是要知道对某个对象调用成员函数的时候有个 this

线段树(2)――区间修改

匿名 (未验证) 提交于 2019-12-02 23:43:01
温馨提示:本文只对刘汝佳《训练指南》做注释,将博主初学时难懂的,认为重要的解释一下,只求自己记住它的打法和用法,如有不懂,随意联系,谢谢 关于此文的注释问题 : 有些博主读书时遇到的问题,解释就近写在文末,但在阅读时没有 对代码 产生疑惑的,我都木有注释... 将sum[o]定义为:“如果只执行节点o及其子孙节点的 add 操作, 节点o对应区间中所有数的和” 我的理解是: 只管节点o的sum值, 而 add 值不用下传至子孙节点(这样很费时), 不懂的可以好好看看下面的query函数中的实现 //维护节点o, 它对应区间为[L, R] void maintain(int o, int L, int R) { int lc = o*2, rc = o*2+1; sumv[o] = minv[o] = 0; if(R > L) {//疑惑 1 : 为什么这是R>L? 万一它是叶子节点呢 sumv[o] = sumv[lc] + sumv[rc]; minv[o] = min(minv[lc], minv[rc]); } minv[o] += addv[o]; sumv[o] += addv[o]*(R-L+1); //考虑add操作 } 在执行add操作时,哪些节点需要调用上述maintain函数? 很简单 (我不觉得...), 递归访问到的结点 都要调用,并且是 在递归返回后调用