线段树

【线段树】[雅礼集训 2017 Day1]市场

空扰寡人 提交于 2019-12-03 05:23:30
\(Description\) 从前有一个贸易市场,在一位执政官到来之前都是非常繁荣的,自从他来了之后,发布了一系列奇怪的政令,导致贸易市场的衰落。 有 \(n\) 个商贩,从 \(0\sim n-1\) 编号,每个商贩的商品有一个价格 \(a_{i}\) ,有两种政令;同时,有一个外乡的旅客想要了解贸易市场的信息,有两种询问方式: \(Input\) 第一行为两个空格隔开的整数 \(n,q\) 分别表示商贩个数和政令 \(+\) 询问个数。 第二行包含 \(n\) 个由空格隔开的整数 \(a_{0}\sim a_{n-1}\) 接下来 \(q\) 行,每行表示一个操作,第一个数表示操作编号 \(1\sim 4\) ,接下来的输入和问题描述一致。 \(Output\) 对于每个 \(3、4\) 操作,输出询问答案。 \(Sample Input\) 10 10 -5 -4 -3 -2 -1 0 1 2 3 4 1 0 4 1 1 5 9 1 2 0 9 3 3 0 9 4 0 9 3 0 1 4 2 3 3 4 5 4 6 7 3 8 9 \(Sample Output\) -2 -2 -2 -2 0 1 1 \(HINT\) 对于 \(30\%\) 的数据, \(n,q\le10^3\) ; 对于 \(60\%\) 的数据,保证数据随机; 对于 \(100\%\) 的数据, \

暂且叫它-\"蒙伪树\"-吧-

痞子三分冷 提交于 2019-12-03 05:16:19
今天我看到 o oo 聚聚画线段树,但是ta画的不是很好,于是本人饶有兴趣的研究了那棵无聊的树。 但是它并不满足树的性质,于是命名为伪树。 为了防止太弱和别的巨型数据结构撞名导致尴尬,于是又挂了自己$id$的一部分,叫"蒙伪树"($\text{Miemeng's Erroneous Tree}$)可以简称$\text{MET}$。 其实是样辉三角? 我伪了。 先来张图: 有啥用: 显然没啥用,可以用上面多的要命的节点维护区间最值,或是一些其他的信息。 时间复杂度: 建树:$O(N^2)$ 查询:$O(1)$ 修改:$O(N)$ 所以它是暴力,恩? 空间复杂度: $O(N^2)$ (为什么我不开二维数组?) 一些乱七八糟的性质: 1> 当我们将$LCA$的编号标记为两个数取平均值后的值,这样每一个值就控制了一些点。 这样就可以建一棵新树。 别问我为什么又画了一个(上面的图太丑了,而且我把它丢了) 这样就变成前缀和啦,所以我们可以得出结论,前缀和是对$\text{MET}$的优化(其实$\text{MET}$是对大部分数据结构的劣化(捂脸 于是对前缀和进行位运算优化,得到树状数组。 然后我们发现里面有很多点是无效的,于是删除一些节点。 发现它(像)是线段树。 于是我们又得出结论,线段树也是对$\text{MET}$的优化。 最后我扯一句,如果用倍增优化可以把$\text{MET}

[POI2011]ROT-Tree Rotations 题解

ⅰ亾dé卋堺 提交于 2019-12-03 05:10:57
题面 这道题咋看都是无法从dp入手,那么就从数据结构入手!; 首先你要会权值线段树和线段树合并。 然后你要知道: 对于任意一个节点,交换左右子树对当前节点和前面的所有节点 没有影响 。 因为这是 前序遍历 :根节点->左子树->右子树。可以看到,交换左右子树 对前面的节点无影响 。 我们清楚,交换子树只会对 该逆序对横跨左右子树 这种情况产生影响。因此,我们只需要在 合并线段树 的过程中统计交换子树的逆序对个数ans1和不交换子树的逆序对个数ans1,取 min(ans1,ans2) 累加到答案中就行了。 每一次合并线段树时,递归到除了叶节点的所有节点,都要累加逆序对个数 u,v 。 需要注意,我们能够这样计算是因为无论左右儿子怎么交换,影响的都只有当前部分的逆序对个数,而不会影响深度更浅的节点的值。 注意要回收内存,否则会MLE #include <bits/stdc++.h> #define inc(i,a,b) for(register int i=a;i<=b;i++) using namespace std; int n; class node{ public: long long lson,rson,sum; }tree[200010*13]; long long ans1,ans2; int now=0; int merge(int x,int y,int l,int

poj2528 (线段树+离散化)

二次信任 提交于 2019-12-03 05:08:40
传送门:: http://poj.org/problem?id=2528 题意: 在墙上贴海报,海报可以互相覆盖,问最后可以看见几张海报 数据:1 <= i <= n, 1 <= l i <= ri <= 10000000;1 <= n <= 10000 思路:离散化+线段树 离散化 定义:: 把无限空间中有限的个体映射到有限的空间中去,以此提高算法的时空效率。 (by百度百科) 通俗点说就是 在不改变数据的可用性质下缩小其范围,一般只关注他们的相对大小 但对于本题而言普通离散化不行,我们还要维护一个性质,那就是是否相邻,举个例子就会明白 [1,5] [1,2] [4,5]离散后[1,4] [1,2][3,4]这样做的答案为2(原因就是二三区间归为一起,导致覆盖第一区间),而实际上答案为3 那我们应该在差值大于1的两个数之间插入一个数,避免相邻的归为一个区间 所以 离散化两性质:大小关系和是否相邻 线段树: 只需开一个数组来标记出现过的颜色即可 1 //#include<bits/stdc++.h> 2 #include<stdio.h> 3 #include<algorithm> 4 #include<string.h> 5 #include<string> 6 #include<iostream> 7 #define ll long long 8 const int maxn

AcWing 353 雨天的尾巴

女生的网名这么多〃 提交于 2019-12-03 05:08:36
写在前面 居然没有树剖的题解…… 我来水一发 题目描述 深绘里一直很讨厌雨天。 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮食被弄得一片狼藉。 无奈的深绘里和村民们只好等待救济粮来维生。 不过救济粮的发放方式很特别。 有 n 个点,形成一个树状结构。 有 m 次发放操作,每次选择两个点 x,y,对 x 到 y 的路径上(包括 x,y)的每个点发放一袋 z 类型的物品。 求完成所有发放操作后,每个点存放最多的是哪种类型的物品。 输入格式 第一行两个正整数n,m,含义如题目所示。 接下来n-1行,每行两个数(a,b),表示(a,b)间有一条边。 再接下来m行,每行三个数(x,y,z),含义如题目所示。 输出格式 共n行,第i行一个整数,表示第i座房屋里存放的最多的是哪种救济粮,如果有多种救济粮存放次数一样,输出编号最小的。 如果某座房屋里没有救济粮,则对应一行输出0。 数据范围 \(1≤n,m≤100000\) , \(1≤z≤10^9\) 样例 输入样例 5 3 1 2 3 1 3 4 5 3 2 3 3 1 5 2 3 3 3 输出样例 2 3 3 0 2 算法1 线段树合并 有大佬已经写了,我就懒得赘述 #include <bits/stdc++.h>

线段树的复杂操作

China☆狼群 提交于 2019-12-03 05:05:07
一,线段树做区间乘法 首先要明白,乘法操作高于加法操作 一般的话会开long long ,要去模 对于一个节点o,我们设区间和为sum[o],加法标记为add[o],乘法标记为mul[o] mul标记的初始值是1,add标记初始值是0 在修改值的时候,add的维护需要累加,mul的维护需要累乘 此时当我们进行区间加的时候,一切照旧 但是当进行区间乘法的时候 儿子节点的add标记分别先乘上父亲节点的乘标记再加上父亲节点的加标记 (因为父亲节点的加标记已经乘上了一些乘标记了,不需要再乘一次) 儿子节点的乘标记直接乘上父亲节点的乘标记。 1 void Mul(ll o,ll l,ll r,ll x,ll y,ll k) 2 { 3 if(x<=l && y>=r) 4 { 5 add[o]=add[o]%mod*k%mod;//相当于加了k个add[o] 6 sum[o]=sum[o]*k%mod%mod; 7 mul[o]=mul[o]*k%mod; 8 return; 9 } 10 ll mid=(l+r)>>1; 11 down(o,l,r,mid); 12 if(x<=mid) Mul(o<<1,l,mid,x,y,k); 13 if(y>=mid+1) Mul(o<<1|1,mid+1,r,x,y,k); 14 sum[o]=(sum[o<<1]+sum[o<<1|1])

线段树基础

▼魔方 西西 提交于 2019-12-03 04:58:39
一,什么是线段树 线段树是一种二叉搜索树,它将一个区间划分成一些单元区间 每个单元区间对应线段树中的一个叶结点 将[1,n]分解成若干特定的子区间(数量不超过4*n) 用线段树对“编号连续”的一些点,进行修改或者统计操作,修改和统计的复杂度都是O(log2(n)) 用线段树统计的东西,必须符合 区间加法 , 也就是说,如果已知左右两子树的全部信息,比如要能够推出父节点 否则,不可能通过分成的子区间来得到[L,R]的统计结果 一个问题,只要能化成对一些“连续点”的修改和统计问题,基本就可以用线段树来解决了 解决问题时常会用到离散化 由上图可知,每个节点的左孩子区间范围为[l,mid],右孩子为[mid+1,r] 对于结点k,左孩子结点为2*k(k<<1),右孩子为2*k+1(k<<1|1) 来源: https://www.cnblogs.com/adelalove/p/11778751.html

线段树动态开点+树链剖分BZOJ4999

点点圈 提交于 2019-12-03 02:48:36
以每个一个颜色开一颗线段树,内部以dfs序作为线段树节点,权值代表出现次数,维护线段树区间和 #include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> #include<map> using namespace std; const int maxn = 2e5+6; struct edge{ int next,to; }e[maxn*2]; struct node{ int l,r; int w; }tree[maxn*50]; int head[maxn],siz[maxn],top[maxn],son[maxn]; int d[maxn],fa[maxn],id[maxn],rk[maxn],dfn[maxn],c[maxn]; int n,m,uu,vv,tot,cnt; int rt[maxn]; map<int,int>p; void add(int x,int y){ e[++cnt].next=head[x]; e[cnt].to=y; head[x]=cnt; } void dfs1(int u,int f,int depth){ d[u]=depth; fa[u]=f; siz[u]=1; for (int i=head[u];i;i=e[i].next){ int v

P4556 雨天的尾巴 线段树合并

主宰稳场 提交于 2019-12-03 02:20:33
使用线段树合并,每个节点维护一棵权值线段树,下标为救济粮种类,区间维护数量最多的救济粮编号(下标)。所以每个节点答案即为 \(tre[rot[x]]\) 。 然后运用树上点的差分思想,对于分发路径 \(u,v\) ,我们在 \(u\) 上+1,在 \(v\) +1,在 \(lca(u,v)\) 处-1,在 \(fa(lca)\) 处-1,最后统计时自底向上做树上前缀和、线段树合并即得当前节点信息。 需要注意的是,在合并时可能会出现 \(tre[rot[x]]\) 不为 \(0\) ,但是 \(sum[rot[x]]\) 为 \(0\) 的情况,这时候表示编号为 \(tre[rot[x]]\) 的救济粮数量为 \(0\) ,所以此时应该将 \(ans[x]\) 更新为 \(0\) (根据题意)。 #include <cstdio> #include <algorithm> #define MAXN 100001 #define LOG 19 #define mxr 100000 using namespace std; inline int read(){ char ch=getchar();int s=0; while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9') s=s*10+(ch^'0'),ch=getchar()