随笔分类 -  线段树 树状数组 RMQ

摘要:题意:忽略题目背景,就是要收集最多的钱, 如果第i天拿到了si 的钱, 那么第i+x[i] 天 到 第i + y[i] - 1 天必须再拿一次,否则就再也拿不到钱了,当然,第i +x[i]天之前也是拿不到的, 题目要求第一天必须拿。。分析:一开始想到了用记忆化搜索,代码很短, 一下就敲完了, 结果也果断超时了后来想到了按记忆化搜索的思路,直接从最后一天开始算起,用dp[i] 表示第i天拿到了钱之后,到第n天为止,最多拿到的钱数,那么dp[i] = s[i] + max(dp[j]) (i + x[i] <= j <= i + y[i] - 1)到这一步就很明显了,这里涉及到了区间最 阅读全文
posted @ 2013-04-06 15:48 枕边梦 阅读(269) 评论(0) 推荐(0)
摘要:题意:给定一棵有根树,节点标号为1 ~ n, 根节点固定为1, 每个节点初始值为0。 总共给定M 个操作操作1:o node 将以node 为根的子树的所有节点的值取反,其实就是一个异或操作操作2:q node 问以node为跟的子树的所有节点值为1的总数分析:对做过LCA 转RMQ的应该能想到一点,如果从根节点深搜一遍得到的欧拉序列,仔细观察一下可以发现,每个节点在欧拉序列上其实对应这一个区间,这样就将对子树的操作转移到了对区间的操作。 做完这一步处理之后,熟悉线段树的同学应该就可以解决了,事实上接下来的操作比之前比赛的一道线段树很像。。线段树的每一个节点保存该节点对应区间上1的个数cn.. 阅读全文
posted @ 2013-04-03 12:41 枕边梦 阅读(190) 评论(0) 推荐(0)
摘要:题意:题目的意思很简单,给定一个序列,求最大递增子序列的长度,同时相邻俩项下标的差值必须大于d分析:就题意而已,只是在最长递增子序列的加了一个限制条件,沿用最长递增子序列的思路,则dp[i] = max(dp[i], dp[j] + 1), (i - j > d && a[i] > a[j])从状态转移方程可以看出,问题的关键在于维护区间(0,i - d - 1) 最大的dp值,且对应值小于a[i]我们换一种角度,用a[i]的值作为下标,保证当前区间(0, a[i] - 1)中的dp值都满足 (i - j > d) , 那么dp[i] 就等于区间(0, a[i 阅读全文
posted @ 2013-04-01 21:38 枕边梦 阅读(307) 评论(0) 推荐(0)
摘要:题意:给定一个序列,要求完成俩个操作1)查询区间[l,r] 的和2)区间更新, 区间每一个数都异或一个数x对每一个查询输出结果分析:对区间更新和查询这种操作,比较明显的就想到了线段树。区间查询比较简单,关键是区间更新该如何解决。我们为线段数的每一个节点开一个cnt[20] 的数组,保存该区间内每一个数二进制位上1的个数,cnt[0] 表示对应区间内所有数第一个二进制数上1的个数和,这样,针对每一个异或操作,我们可以这样解决: for(int i = 0; i < L; ++i) { if(!(v & ( 1 << i) )) continue;//针对异或操作的性质, 阅读全文
posted @ 2013-03-10 13:14 枕边梦 阅读(201) 评论(0) 推荐(0)
摘要:题意:给定一棵有n(n < 100001 )个结点的带边权的树,处理以下一共q(q < 100001)个操作:1,改变树的一条边的权;2,求给定点和某点的距离,后者是编号为1的结点,若是第一次执行操作2,否则为上次执行操作2的给定点。分析:如果没有操作1 的话,也就是边的权值没有改变的话,是很常见的LCA转RMQ问题; 如果边 的权值发生改变了的话,是否意味预处理出来的数据都没用了呢?其实不然,改变一条边的权值,对那些边造成影响是固定的,也就是说,如果欧拉序列固定了,改变了一条边的权值,那么整个子树的dis[]值(到根节点的距离)都改变了,而在欧拉序列中,整个子树是连续的,,只需要 阅读全文
posted @ 2012-08-13 22:17 枕边梦 阅读(213) 评论(0) 推荐(0)
摘要:很明显的状态转移方程式:dp[i]=max(dp[j])+g[i], (0<=j<i && h[i]>h[j])不过注意到题目中n的范围有10000 ,n^2的算法…………我们可以用线段树优化一下先对高度离散化,作为线段树左右区间的端点这样,求dp[i]时,只需要查出高度(0,h[i]-1]范围内的最大值t,那么dp[i]=t+g[i], 之后,接着,更新线段树中高度为g[i],值为dp[i]的点View Code #include<iostream>#include<algorithm>#include<string>#i 阅读全文
posted @ 2012-04-13 16:30 枕边梦 阅读(410) 评论(0) 推荐(0)
摘要:题意:将一个给定的数删除m位,求剩下的数的最小值分析:用RMQ,假设原来数字的长度为n,每次从一个区间里面取出一个最小值,取n-m即可View Code #include<iostream>#include<algorithm>#include<string.h>#include<math.h>using namespace std;int n,dp[1010][20];char B[1010];void init_RMQ(){ memset(dp,0,sizeof(dp)); for(int i=0;i<n;i++) dp[i][0]=i 阅读全文
posted @ 2012-03-08 11:28 枕边梦 阅读(265) 评论(0) 推荐(0)
摘要:纠结了N久 的题目,终于过了,LCA转RMQ的思想首先,求出欧拉序列F[]以及深度序列B[],同时,用pos[]记录每一个节点第一次出现的位置(在欧拉序列中的位置);之后,就是LCA转化为RMQ的问题了,LCA(u,v)=F(RMQ(B,pos[u],pos[v]))ps:pos[u]<pos[v]:深度序列中,2个点之间的最小深度就是最近公共祖先View Code #include<iostream>#include<algorithm>#include<vector>#include<math.h>#define MAXN 10010u 阅读全文
posted @ 2012-02-29 13:00 枕边梦 阅读(338) 评论(0) 推荐(0)
摘要:树状数组的一维和二维模板模板int lowbit(int x){ return x & (-x);}void modify(int x,int add)//一维{ while(x<=MAXN) { a[x]+=add; x+=lowbit(x); }}int get_sum(int x){ int ret=0; while(x!=0) { ret+=a[x]; x-=lowbit(x); } return ret;}... 阅读全文
posted @ 2012-02-27 22:18 枕边梦 阅读(4229) 评论(0) 推荐(2)
摘要:题意:对一棵树上的点进行实时的增减权值和询问子树的权值和首先:单点修改以及子树查询。对整棵树dfs一遍,记录下欧拉序列。这样有一个好处,就是每一棵子树在这个序列中都是连续的,同时记录下每一棵子树的起始序列号,这样,对子树的查询就变成了一个连续区间的查询了,运用树状数组就可以解决了。那最大生成树呢?“Sailormoon girls want to cut extra branches by using minimum cost, after that..."额,这里要将多余的边减掉,用最小cost,也就是减掉之后,变成了一棵最大生成树。郁闷呀,一开始以为题目所给的cost是没用……… 阅读全文
posted @ 2012-02-26 17:02 枕边梦 阅读(563) 评论(0) 推荐(0)
摘要:题意:给你一个序列,之后又Q次询问,每次都是询问一个区间内最大值与最小值的差分析:RMQ,求区间最值RMQ(Range Minimum/Maximum Query)问题: RMQ问题是求给定区间中的最值问题。当然,最简单的算法是O(n)的,但是对于查询次数很多(设置多大100万次),O(n)的算法效率不够。可以用线段树将算法优化到O(logn)(在线段树中保存线段的最值)。不过,Sparse_Table算法才是最好的:它可以在O(nlogn)的预处理以后实现O(1)的查询效率。下面把Sparse Table算法分成预处理和查询两部分来说明(以求最小值为例)。 预处理: 预处理使用DP的思想.. 阅读全文
posted @ 2012-02-16 20:37 枕边梦 阅读(244) 评论(0) 推荐(0)
摘要:题意:给定一个数字串,求最小的分段数m(每段个数按数字窜顺序切割,余数丢弃)求各个段中的最大值之和超过k的最小m是多少分析:先求出m的上界和下界,二分枚举m 之后就是一个区间求最值的问题了,还不会RMQ,只能用线段树求了线段树版#include<iostream>#include<algorithm>#include<queue>#define MAXN 200000+10using namespace std;struct node{ int l,r,maxn;}p[MAXN<<2];int a[MAXN],n;void bulid(int s 阅读全文
posted @ 2012-02-16 16:51 枕边梦 阅读(635) 评论(0) 推荐(0)
摘要:题意:给定N个区间,求最后区间内最长连续区间的长度以及最长不覆盖的长度分析:本来是可以直接暴力的,可是想练一下最近线段树到底做得怎么样了,所以就用线段树试了一下,哎,改了俩个多小时,终于出来了。注意:对区间长度的概念还是……因为离散化之后区间长度的求法变了,所以再求半个区间长度的时候,出错了/*ID: nanke691LANG: C++TASK: milk2*/#include<iostream>#include<fstream>#include<algorithm>#include<string.h>#define maxn 20000usin 阅读全文
posted @ 2011-10-12 22:36 枕边梦 阅读(219) 评论(0) 推荐(0)
摘要:hdu 3265 Posters天呐,这算什么啊?ans+=1ll*len[1]*(ss[i+1].x-ss[i].x);//这里忘了转成long long型了,结果改了一整晚,可是之前没碰过需要转的呀(不解,求解释),orz!!半信半疑的改完提交了,结果真的过了,再把之前的代码改了一下,还是过了,郁闷!!!题意:同样是求N个矩形的面积并,只不过,矩形中间存在一个空矩形……最直观的方法就是将一个带空矩形的矩形分成四个矩形,再按照求面积并的方法求一遍就是了,需要离散化的#include<iostream>#include<algorithm>#define maxn 20 阅读全文
posted @ 2011-10-03 02:15 枕边梦 阅读(183) 评论(0) 推荐(0)
摘要:hdu1255 覆盖面积题意:求N个矩形中,求被覆盖至少俩次的面积和分析:一开始以为用总面积减去面积并就可以了,可是想了想,当面积被覆盖了俩次以上,就漏减 了,所以只能老老实实算了。具体方法跟求面积并十分类似,求面积并时,排完序之后,每次插入一条线段之后,求出整个区间当前被覆盖的总长度再乘以 前后俩条线段的水平距离;而这道题目,每次插入一条线段之后,求整个区间当前被覆盖至少俩次的总长度再乘以水平距离,;要求整个区间被覆盖至少俩次的总长度,只需在原有代码上添加几个更新而已,具体代码附了解释#include<iostream>#include<algorithm>#defi 阅读全文
posted @ 2011-10-02 21:00 枕边梦 阅读(633) 评论(0) 推荐(0)
摘要:求N个矩形的面积并,这个比周长并要简单的多,不过涉及到了离散化,根据相对大小,给对应的double型数据编号,插入是再二分查找编号即可也可以用矩形切割去做,代码简单很多矩形切割#include<iostream>#include<algorithm>#include<string>#include<math.h>using namespace std;const int N = 100+10;struct rec{ double p1[2],p2[2];}r[N];rec rr[N*N];int total;inline double get_a 阅读全文
posted @ 2011-10-02 19:10 枕边梦 阅读(286) 评论(0) 推荐(0)
摘要:这困扰我N久的题目,终于过了。求N个矩形的周长并,一开始觉得太遥不可及了,感觉好复杂 ,总想先搞懂计算的方法,再看大牛的方法,可是N久之后,对那个方法还是一知半解;今天结合了代码还有下面俩副图之后,突然觉得豁然开朗啦,自己敲了一遍代码……重点:我觉得,最重要的就是理解下面三个式子了,理解了之后,就知道该怎么建立线段树了;ans+=num[1]*(ss[i+1].x-ss[i].x);(横边)ans+=abs(len[1]-last);(竖边)last=len[1];对所有的竖边按照x值从小到排序,之后按顺序插入线段树;ans累加的是最终结果,num[1]是整个区间内连续的线段数(将竖边投影到y 阅读全文
posted @ 2011-10-02 16:31 枕边梦 阅读(1248) 评论(1) 推荐(0)
摘要:hdu 1556 Color the ball中文的题目,题意就不说了用线段树做了,不过有不用线段树的方法,不过我解释不来贴代码吧#include<iostream>#define MAXN 100010using namespace std;int p[MAXN<<2];void insert(int k,int s,int t,int l,int r){ if(l<=s && t<=r) { p[k]++; return ; } int kl=k<<1,kr=kl+1,mid=(s+t)>>1; if(l<= 阅读全文
posted @ 2011-09-24 00:31 枕边梦 阅读(224) 评论(0) 推荐(0)
摘要:pku 3667 Hotel题意:去掉题目背景,就是给你一个[1,n]的区间,初始时,整个区间为空,在区间进行俩个操作:1) 输入 1 D :在区间上从左到右找出第一个连续的长度为D 的空间,并将该区间填满,输出区间的端点,若不存在,输出02) 输入 2a b: 将区间[a,a+b-1] 填满分析:很明显是用线段树进行维护,关键是每一个节点需要保存该区间的些什么值?这里我们可以想到,因为要找出的是一个连续的空区间的左端点,这个区间可能分布在左儿子上,右儿子上,或者可能在俩边都有,这样给查询带来了极大的不方便。不过,我们可以充分利用线段树的结构特点,通过保存与区间左右端点有关的信息,就可以遍历整 阅读全文
posted @ 2011-09-23 09:56 枕边梦 阅读(378) 评论(0) 推荐(0)
摘要:hdu 2795 Billboard题意:h*w的木板,放进一些1*L的物品,求每次放空间能容纳且最上边的位子思路:总共有h个叶节点,表示有h个区间,线段树的一个域表示该区间还能容纳的最大物品,每次找到最大值的位子(先找左边),然后减去L线段树功能:query:区间求最大值的位子(直接把update的操作在query里做了)#include<iostream>#include<algorithm>#define MAXN 222222using namespace std;int p[MAXN*4];int query(int k,int x,int s,int t){ 阅读全文
posted @ 2011-09-22 17:59 枕边梦 阅读(136) 评论(0) 推荐(0)