线段树

kuangbin专题七 线段树【从入门到熟练】【13题】

生来就可爱ヽ(ⅴ<●) 提交于 2019-12-21 07:16:53
【POJ 2528 Mayor's posters】 每个海报会覆盖一段连续的区间,所以这是个区间覆盖问题,可以用线段树。 但硬上nlogm虽然不会tle,但会mle,所以要离散化。 在[1,10000000]的这个瓷砖里,只有10000级别的修改,我们可以想象出整个区间可以被划分为多个被cover情况【一致】的小区间。那什么样的小区间被cover情况一致呢,所有端点排序后相邻端点构成的区间被cover情况是一致的,为什么呢?因为每次的覆盖都必定是两两端点间的。 这样的话每次cover就是cover这个海报start所代表的区间到end所代表的区间。 【注意要从后往前贴海报】 #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<string> #include<stack> #include<map> #include<iomanip> #include<algorithm> #include<vector> #define INF 2e9 #define MAXN 20010 #define maxnode 100000 #define ll long long #define lowbit(x) (x&(-x)) int dx[4]={0,0

AC自动机fail树上dfs序建线段树+动态memset清空

蓝咒 提交于 2019-12-20 04:21:27
题意: http://acm.hdu.edu.cn/showproblem.php?pid=4117 思路: https://blog.csdn.net/u013306830/article/details/77586562 主要就是卡你内存,AC自动机的字典树得要用了再清空。 代码有点长吧。。。 1 #include <cstdio>//sprintf islower isupper 2 #include <iostream>//pair 3 #include <string.h>//strstr substr strcat 4 #include <queue>//priority_queue<int, vector<int>, greater<int> > q;//less 5 using namespace std;//next_permutation(a+1,a+1+n);//prev_permutation 6 #define mem(a,b) memset(a,b,sizeof(a)) 7 #define pr printf 8 #define sc scanf 9 #define ls rt<<1 10 #define rs rt<<1|1 11 const int N=3e5+10; 12 13 char s[N]; 14 int pos[20004],val

(线段树 点更新 区间求和)lightoj1112

江枫思渺然 提交于 2019-12-19 21:06:46
链接: http://acm.hust.edu.cn/vjudge/contest/view.action?cid=88230#problem/D (密码0817) Description Robin Hood likes to loot rich people since he helps the poor people with this money. Instead of keeping all the money together he does another trick. He keeps n sacks where he keeps this money. The sacks are numbered from 0 to n-1 . Now each time he can he can do one of the three tasks. 1) Give all the money of the i th sack to the poor, leaving the sack empty. 2) Add new amount (given in input) in the i th sack. 3) Find the total amount of money from i th sack to j th sack. Since he is not a

线段树

不打扰是莪最后的温柔 提交于 2019-12-18 18:52:25
简介 注意事项 开数组要开4倍大小,可以不用判断结束的边界条件。 代码 #include <cstdio> #include <iostream> #include <algorithm> #define maxn 100000 #define inf -1000000 using namespace std; //maxv数组记录每一个区间内的最大值 //sum数组记录区间和 //add数组记录这个区间需要被加的数(lazy) int a[maxn], sum[maxn << 2], add[maxn << 2]; //ln左子树节点个数 //rn右子树节点个数 void pushdown(int id, int ln, int rn){ if(add[id]){ add[id << 2] += add[id]; add[id << 2 | 1] = add[id]; sum[id << 2] += add[id] * ln; sum[id << 2 | 1] += add[id] * rn; add[id] = 0; } } //建树 void build(int id, int l, int r){ if(l == r){ sum[id] = a[l]; add[id] = 0; return; } int mid = (l + r) >> 1; build(id << 1,

Gorgeous Sequence(HDU5360+线段树)

大兔子大兔子 提交于 2019-12-18 07:55:14
题目链接 传送门 题面 思路 对于线段树的每个结点我们存这个区间的最大值 \(mx\) 、最大值个数 \(cnt\) 、严格第二大数 \(se\) ,操作 \(0\) : 如果 \(mx\leq val\) 则不需要更新改区间; 如果 \(se\leq val<mx\) 则只需将区间最大值进行更新,此时 \(sum=sum-cnt\times (mx - val)\) ; 如果 \(val<se\) 则递归下去。 (详情请看吉老师 \(ppt\) 因为一开始加了个 \(lazy\) 标记导致情况复杂化,且不好处理,对拍好久才发现。 代码实现如下 #include <set> #include <map> #include <deque> #include <queue> #include <stack> #include <cmath> #include <ctime> #include <bitset> #include <cstdio> #include <string> #include <vector> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long LL; typedef pair<LL

线段树&N - I Hate It

試著忘記壹切 提交于 2019-12-18 05:28:31
转载处: http://blog.csdn.net/metalseed/article/details/8039326 一:线段树基本概念 1:概述 线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(lgN)! 性质:父亲的区间是[a,b],(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b],线段树需要的空间为数组大小的四倍 2:基本操作(查询区间最小值) 线段树的主要操作有: (1):线段树的构造 void build(int node, int begin, int end); 主要思想是递归构造,如果当前节点记录的区间只有一个值,则直接赋值,否则递归构造左右子树,最后回溯的时候给当前节点赋值 void build(int node, int begin, int end) { if (begin == end) segTree[node] = array[begin]; /* 只有一个元素,节点记录该单元素 */ else { /* 递归构造左右子树 */ build(2*node, begin, (begin+end)/2); build(2*node+1, (begin+end)/2+1, end); /*

线段树解LIS

…衆ロ難τιáo~ 提交于 2019-12-18 03:55:52
先是nlogn的LIS解法 /* LIS nlogn解法 */ #include<iostream> #include<cstring> #include<cstdio> using namespace std; //结尾的数字越小,说明相同长度下当前序列越优 int lis[100005],a[100005];//lis[i]表示长度为i的最优的结尾数 int n,len=0; int find(int x){//找到lis中第一个大于等于x的数的下标 int l=0,r=len; while(l<r){ int mid=l+r>>1; if(lis[mid]>=x) r=mid; else l=mid+1; } return l; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); lis[1]=a[1];len++; for(int i=2;i<=n;i++){//按顺序处理每一个数 if(a[i]>lis[len])//可以更新新长度了 lis[++len]=a[i]; else{//对于一个a[i],可以更新以它为结尾的最长lis的结尾 int pos=find(a[i]);//找到lis中第一个大于等于a[i]的数,pos也是以那个数结尾的长度 lis[pos]=a[i]; /

线段树合并

ぐ巨炮叔叔 提交于 2019-12-18 03:54:51
线段树合并,就是将已有的两棵线段树合并为一棵,相同位置的信息整合到一起,通常是权值线段树 比较裸的,就是将一棵线段树的每一个位置取出来插入另一棵中 但比较高效的线段树合并可以参照可并堆的合并方式 线段树合并的原理十分简单,具体步骤如下: 对于两颗树的节点u和v ①如果u为空,返回v ②如果v为空,返回u ③否则,新建节点t,整合u和v的信息,然后递归合并u和v的左右子树 代码如下: int merge(int u,int v){ if (!u) return v; if (!v) return u; int t = ++cnt; sum[t] = sum[u] + sum[v]; ls[t] = merge(ls[u],ls[v]); rs[t] = merge(rs[u],rs[v]); return t; } 容易发现,这样合并的复杂度取决于两棵线段树重合的部分的大小 每有一个位置权值同样存在,就要 \(O(logn)\) 的复杂度 不过,由于权值线段树中被更新的位置通常很均匀分布,所以合并的两棵线段树通常具有很小的相似性 可以用这一道水题入门线段树合并 BZOJ4756 #include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #define LL

线段树+LIS

时光毁灭记忆、已成空白 提交于 2019-12-18 03:54:31
https://vjudge.net/contest/237022#problem/C 题意:有一组数1~n,按顺序给出每个数需要插入的位置(即第i数代表数字i要插入当前序列的第ai个),组合成新的序列,问每插入一个新的数,当前序列的最长上升子序列为多长? 解法:首先用线段树插空法将数列的最终位置还原出来,得到1~n的对应位置。而对于每次操作,当前序列的最长上升子序列长度等于1~n对应位置所构成的序列的最长上升子序列长度(可手算推导)。由于操作顺序从1~n,所以第i次操作只要对前i个数(即1~i)对应的位置组成的序列求LIS即可。 线段树插空法:从最后一个数开始,逆推每个数的对应位置,因为放置最后一个数时,其位置已经可以确定。用数组s记录每个操作数的插入位置,则逆推过程中,s[i]表示第i个前面需要留有s[i]个空格。线段树数中的a[i].l,a[i].r,a[i].n分别代表第i个结点的左右边界以及对应区间的空格数。记得初始化,即l==r时,该区间空格数为1。 LIS计算:用数组dp[]记录当前序列的最长上升子序列,len记录当前的最长长度,ans[i]记录数字i对应的位置。利用二分的方法,从1~n循环,每次寻找dp数组中第一个大于等于ans[i]的数,用k记录该数在dp数组中的位置(实质上k为ans[i]在满足序列上升的条件下放到dp数组中的对应位置),更新len(如果ans

Philosopher(set 线段树合并)

回眸只為那壹抹淺笑 提交于 2019-12-18 03:54:15
直接维护乘积是肯定不可行的, 精度会爆炸, 于是我们来维护对数的和, 最后来计算最高位即可 那么转换成区间求和, 区间排序 区间排序的方式可以采用线段树维护最大递增块来解决,外层用set来维护线段树的区间, 然后利用线段树的合并分裂性质来操作即可 #include<cstdio> #include<algorithm> #include<cstring> #include<queue> #include<iostream> #include<set> #include<cmath> #define ll long long #define M (1 << 18) #define N 20000010 #define double long double const double eps = 1e-8; using namespace std; int read() { int nm = 0, f = 1; char c = getchar(); for(; !isdigit(c); c = getchar()) if(c == '-') f = -1; for(; isdigit(c); c = getchar()) nm = nm * 10 + c - '0'; return nm * f; } double c[M], ver[M]; int num[M], n, m; int