线段树

权值线段树

匿名 (未验证) 提交于 2019-12-03 00:10:02
权值线段树 其实权值线段树在学习主席树的时候我就提到过这个东西。 权值线段树和普通线段树之间区别就是在于 它存储的是 区间内数的个数 所以用这种数据结构我们可以求解一个 完整区间的第k大 ,要注意和主席树的区别。 主席树更加强大!! 学了主席树反过来学权值线段树超简单 直接上例题:黑匣子 https://www.luogu.org/problemnew/show/P1801 1 #include <stdio.h> 2 #include <iostream> 3 #include <algorithm> 4 #include <string.h> 5 #include <vector> 6 #include <map> 7 #include <random> 8 #include <sqlite3.h> 9 10 const int maxn = 3e5 + 10 ; 11 12 int k ; 13 int arr [ maxn ]; 14 int u [ maxn ]; 15 16 struct val_segment_tree { 17 int l , r ; 18 int val ; 19 } tree [ maxn << 2 ]; 20 21 void build ( int nod , int l , int r ) { 22 tree [ nod ]. l = l ;

E - No Pain No Game 线段树 离线处理 区间排序

匿名 (未验证) 提交于 2019-12-03 00:05:01
https://nanti.jisuanke.com/t/41391 这个题目没有很难想,比较暴力,但是要会算复杂度,不会算复杂度,就会觉得自己的算法会超时,实际上不会。 这个题目就是直接暴力求出每一个数的在1e5以内的所有的约数和倍数,然后更新,就像之前写过的 E - No Pain No Game 线段树 离线处理 区间排序 一样 算一下复杂度,调和级数要用两次,一次来求约数一次来求倍数,复杂度都是n*logn 所以平均下来每一个的约数大约就是logn 因为有个两倍的关系所以就是2*logn 线段树的复杂度,每次更新是logn ,要遍历一次 所以总的复杂度就是2*n*logn*logn,这个肯定不会超时的。 #include <cstdio> #include <cstring> #include <cstdlib> #include <queue> #include <algorithm> #include <iostream> #define inf 0x3f3f3f3f using namespace std ; const int maxn = 1e5 + 10 ; typedef long long ll ; vector <int> num [ maxn ]; int a [ maxn ], vis [ maxn ], c [ maxn ], ans [ maxn

【模板】吉司机线段树 HDU 5306 Gorgeous Sequence

匿名 (未验证) 提交于 2019-12-03 00:03:02
也叫小清新线段树,用于解决区间最值修改问题 此题就作为模板好了,模板的话写法是比较精妙的 #include < bits / stdc ++. h > using namespace std ; #define go ( i , a , b ) for ( int i = a ; i <= b ;++ i ) #define com ( i , a , b ) for ( int i = a ; i >= b ;-- i ) #define mem ( a , b ) memset ( a , b , sizeof ( a )) #define int long long const int N = 1000000 + 10 ; int n , m , a [ N ]; struct tree { //其实更新标记和最大值可以二合一 //若更新成功则最大值就是标记,若没有更新下传最大值也不会更新子区间 int l , r , mx , se , c , sum ; #define l ( i ) t [ i ]. l #define r ( i ) t [ i ]. r #define c ( i ) t [ i ]. c #define mx ( i ) t [ i ]. mx #define se ( i ) t [ i ]. se #define sum ( i ) t [

uoj #46[清华集训2014]玄学

匿名 (未验证) 提交于 2019-12-03 00:03:02
uoj 因为询问是关于一段连续区间内的操作的,所以对操作构建线段树,这里每个点维护若干个不交的区间,每个区间 \((l,r,a,b)\) 表示区间 \([l,r]\) 内的数要变成 \(ax+b\) 每次把新操作加入线段树中下一个叶子,然后如果某个节点里所有操作都加进去了,就条到父亲,把两个儿子的信息合并到父亲上.这里合并就是把两个区间集合合并成一个,例如两个区间 \([a,c]\) 和 \([b,d](a\le b\le c\le d)\) 会合并成 \([a,b),[b,c),[c,d]\) .合并出来的区间如果属于两个集合中元素的交,那么对应的 \(ax+b\) 就是 \(a_r(a_l+b_l)+b_r=a_ra_lx+a_rb_l+b_r\) . 查询时依次找到对应的线段树节点,然后在区间集合上二分出 \(k\) 元素所在的区间,把 \(ans\) 更新成 \(a*ans+b\) 即可 #include < bits / stdc ++. h > #define LL long long #define uLL unsigned long long #define db double using namespace std ; const int N = 6e5 + 10 ; LL rd () { LL x = 0 , w = 1 ; char ch = 0 ;

区间gcd (带修) 线段树

匿名 (未验证) 提交于 2019-12-02 23:59:01
题目链接: https://ac.nowcoder.com/acm/contest/1033/B 再次吐槽CH 区间gcd再加区间修改。 一般求gcd的时候辗转相除法。 gcd(x,y)=gcd(x,y-x) 那么可以把这个公式推到3个项。 gcd(x,y,z)=gcd(x,y-x,z-y) 可以看出来这是一个差分数列。 原数列为a[],差分数列为b[]。 这样就可以用线段树在b[]上单点修改,l加上d,r+1减去d。 再用树状数组维护出一个c[]数组,区间修改,单点查询a[]数组。 这样的话答案就是 gcd(a[l]+aks_c(l),ask_t(1,1,n,l+1,r)) 就可以较小复杂度处理了。 代码如下: #include < bits / stdc ++. h > #define ll long long using namespace std ; const int maxn = 500001 ; int n , m ; ll c [ maxn ], a [ maxn ], b [ maxn ]; struct node { ll ans ; #define ans ( x ) t [ x ]. ans } t [ maxn << 2 ]; inline ll gcd ( ll x , ll y ){ return y ? gcd ( y , x % y ) : x ;

I Hate It HDU - 1754(线段树找区间最大值)

匿名 (未验证) 提交于 2019-12-02 23:57:01
I Hate It HDU - 1754 题目链接: https://vjudge.net/problem/HDU-1754 题目: 很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。 这让很多学生很反感。 不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩。 Input本题目包含多组测试,请处理到文件结束。 在每个测试的第一行,有两个正整数 N 和 M ( 0<N<=200000,0<M<5000 ),分别代表学生的数目和操作的数目。 学生ID编号分别从1编到N。 第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩。 接下来有M行。每一行有一个字符 C (只取'Q'或'U') ,和两个正整数A,B。 当C为'Q'的时候,表示这是一条询问操作,它询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少。 当C为'U'的时候,表示这是一条更新操作,要求把ID为A的学生的成绩更改为B。 Output对于每一次询问操作,在一行里面输出最高成绩。Sample Input 5 6 1 2 3 4 5 Q 1 5 U 3 6 Q 3 4 Q 4 5 U 2 9 Q 1 5 Sample Output 5 6 5 9 思路:简单修改一下线段树单点修改即可

CCF(除法):线段树区间修改(50分)+线段树点修改(100分)+线段树(100分)

匿名 (未验证) 提交于 2019-12-02 23:57:01
201709-5 这道题有很多种方法来做,最常用的就是线段树和树状数组。 如果使用线段树来做,就会想到区间修改的update函数。但是这里可能会涉及到v是1或者a[j]是0的情况,所以用这种方法会超时,最多50分。 可以修改一下代码,使用点修改来做这道题。在main函数里面增加一个循环,用来判断。 当然,还有一种方法就是树状数组,这种方法和上面这种方法运行时间相差无几,但是代码量大大减少。 需要注意的是,如果v是long long型,最好不要用scanf %lld的方式读入,否则超时。 使用线段树代码: //线段树求解 #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<vector> using namespace std; const int maxn=100005; int n,m; int a[maxn]; long long sum[maxn<<2]; void pushup(int id,int l,int r){ int lc=id<<1; int rc=id<<1|1; sum[id]=sum[lc]+sum[rc]; } void build(int id,int l,int r){ if(l==r){ sum[id]

POJ-2528(线段树+离散化)

匿名 (未验证) 提交于 2019-12-02 23:57:01
Mayor's posters POJ-2528 本题是线段树的区间更新和离散化的结合。 代码中需要注意的就是这里要加入一个去重的操作。而且我这里建树的时候是从1开始的,所以下标的问题要注意。 还有一个需要注意的地方就是根据题目的意思在离散化之后,可能会出现一个问题,就是[1,10],[1, 4],[6,10]这种情况会发生错误。这里直接将4-6看成是连续的,但是实际上着中间还有一个5是属于第一个区间的。所以针对这种情况需要额外加一个点。 还有一个数据量的问题也需要注意。数组尽量开大一点,要不然不是报RE就是WA(亲测)。 //离散化+线段树 #include <iostream> #include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <cmath> using namespace std ; const int maxn = 100014 ; int n , m ; int lazy [ maxn << 2 ]; int num [ maxn << 1 ]; int left1 [ maxn ]; int right1 [ maxn ]; bool vis [ maxn ]; int ans = 0 ; void build ( int id , int l ,

线段树2

匿名 (未验证) 提交于 2019-12-02 23:57:01
先来看一下这道模板题: https://www.luogu.org/problem/P2846 题目大意: 开始所有灯都是暗的, 改变:每次操作可以改变两盏灯之间所有灯的状态(亮变暗,暗变亮), 查询:每次查询两盏灯之间亮的灯的个 数 这道题与线段树1中讲的那道题大部分都是一样的,只有一点, 那就是如何在线段树上区间修改 很明显,如果在线段树上执行n次单点修改,时间复杂度会达到O(n^2logn) 比朴素算法更劣,所以要找到更好的解决办法 我们可以在这棵树上的某些节点上标记lazy 表示这个节点对应的区间都要修改 但该节点的所有后代节点的num值都不修改 只修改当前节点的num值 但由于打上了lazy 下次还能找到该节点 如果查询时遇到lazy标记的节点 就将标记下沉(取消该节点的lazy,并lazy标记上它的两个子节点,再修改它两个子节点的num值) 这样就能用O(logn)完美解决区间修改问题 再具体说一下这道题,这道题需要用上状态压缩 比如将 开、开、关、开、关、关 用二进制数110100来表示 变为十进制就是52,这样就可以用52表示 开、开、关、开、关、关这个状态 这样每个节点num存的就是它表示的区间中的状态对应的十进制数 最后,给大家看一下代码: #include < bits / stdc ++. h > using namespace std ; int num

学习笔记:可持久化线段树(主席树):静态 + 动态

匿名 (未验证) 提交于 2019-12-02 23:56:01
线段树。线段树分享可以看: @秦淮岸 、 @ZYzzz 、 @妄想の岚がそこに 树状数组。 \(BIT\) 分享可以看: @T-Sherlock 、 Chicago 、 @weishengkun 权值线段树:相当于将线段树当成一个 Ͱ ,其中的每一个点所代表的区间相当于 一段值域 。维护的值为这段值域中的一些信息。 例如该图,节点 \(2\) 代表的是值域为 \([1, 2]\) 的区间,节点 \(6\) 代表值域为 \([3, 4]\) 的区间... 可持久化概念: 可持久化实质上就是存储该数据结构 所有的历史状态 ,以达到高效的处理某些信息的目的。 题目链接 :给定长度为 \(N\) 的序列 \(A\) ,有 \(M\) 次询问,给定 \(l_i, r_i, k_i\) ,求在 \([l_i, r_i]\) 区间内第 \(k_i\) 小的数是多少。 \(N <= 10^5, M <= 10^4\) 我们可以建立一颗权值线段树,每个点存储的信息为 该值域区间存在的数的个数 。 因为线段树的性质,所以每个点的左子树的值域区间 $ <= $ 右子树的值域区间。 所以我们先看左子树区间有多少个数,记为 \(cnt_{left}\) 。 如果 \(k_i <= cnt_{left}\) ,说明第 \(k_i\) 小的数一定在左子树的值域内,所以问题便转换为了“在左子树的值域内找第 \(k