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

摘要:题意:给定一个长度为N的序列,现在要求给出一个最长的序列满足序列中的元素严格上升并且相邻两个数字的下标间隔要严格大于d。分析:1.线段树由于给定的元素的取值范围为0-10^5,因此维护一棵线段树,其中[l, r]的信息表示处理完前k个数时,序列最大元素落在[l, r]区间最长上升子序列的长度。从前往后处理给定的数组,处理到第 i 号元素时,更新第 i - d 号元素,这样就能够保证最长上升的序列间隔大于d,更新是需要更新到叶子节点的,但这里更新是单点更新,每次更新的位置是该元素的值,信息就是到该点的最长上升长度。其实仔细分析可以发现这个解法其实是经典的O(n^2)的算法的改进,那个算法需要遍历 阅读全文
posted @ 2013-10-07 16:14 沐阳 阅读(1337) 评论(0) 推荐(0) 编辑
摘要:题意:给定一个N*N的网格,现在M组操作,一种操作时改变网格上的某个单点的权值,另外一种操作是求到一点曼哈顿距离为小于等于k的所有的权值和,初始化网格所有点的权值为0。解法:这题如果没有那些特定的条件,那么就是一个纯净的二维树状数组。对于题目中的要求需要解决的两个问题是:如何将题目中的曼哈顿距离转化为规则的矩形,以及如何避免开辟一个巨大的数组。1.如何转化为矩形问题,这里处理的方法就是把所有的点都旋转45度,需要一个2N*2N的数组数组才能容下坐标转换之后的图,坐标的转化方式为nx = x - y + N, ny = x + y - 1。可以这样看,转化之后的同行相邻两列的坐标差为(-1, 1 阅读全文
posted @ 2013-06-05 19:04 沐阳 阅读(1245) 评论(0) 推荐(0) 编辑
摘要:题意:给定若干根长度为Li的绳子,现在每条绳子只能够切成两份或者不切,问最后最多产生多少根长度相等的绳子?解法:首先明确一定就是每条绳子最多对结果贡献2,因为一条绳子最多切成两份,且当最后结果为其长度的一半时才成立,其余绳子要么贡献为0(长度小于枚举长度),要么贡献为1(长度大于枚举长度不等于2倍的枚举长度)。因此可以枚举这个所切的长度,很容易推出这个长度一定会是某个绳长的一半,由于绳子的长度可能会出现奇数,因此给所有的长度乘以2之后再枚举每条绳子的一半就可以了,使用树状数组初始化前缀和,如果枚举的长度为Lx最后的结果为长度为Lx到MaxL的数量加上长度为2*Lx的绳子数。代码如下:#incl 阅读全文
posted @ 2013-05-31 18:08 沐阳 阅读(369) 评论(0) 推荐(0) 编辑
摘要:题意:给定一棵树,每个节点刚开始的时候都是黑色的,现在有Q组操作,要么是询问当前的树中距离最远的两个黑色的距离是多大,要么是改变某一点的颜色。解法:该题看论文+参考代码搞了一天,感觉模型的转化已经求解的过程都非常的巧妙。1.将这棵树有树形结构选择一个根节点再通过dfs搜索转化线性结构,然后将距离问题用括号来描述。2.使用括号描述后,发现任意两个点的距离是通过对之间的括号序列进行某种消除从非稳定态到达一个稳定态,然后在稳定态中得到距离信息。3.发现括号序列是能够进行合并的,所以线段树被派上了用场。而这明显又是一个动态规划的合并过程,关于区间的线段树题无非要维护这么几个量,单独的区间、左连续的区间 阅读全文
posted @ 2013-05-16 23:07 沐阳 阅读(1578) 评论(0) 推荐(0) 编辑
摘要:题意:存在一个固定编号[1,10^9]的盒子群,每个盒子里面都有一个数Ai(Ai>=0),刚开始的时候不知道各个盒子里面的数是多大。有Q组更新,分别表示[Li, Ri]内最小的元素是多大,现在问Q组操作后,盒子中数字总和最小可以为多少?解法:标准题解是并查集树状数组的解法,谢勇教练也提到可以使用线段树来解。这里就是使用的线段树来求解。由于该题需要离散化点,这里有个地方特别要注意就是不能够建一棵点树,因为如果建立了一棵点树,那么意味着离散化后的叶子区间是单个点,各个相邻的叶子节点之间的区间信息无法保存,也就是这里错了很多次。正确的做法是如果有某组更新是[Li, Ri] Ci,那么就是更新[ 阅读全文
posted @ 2013-05-15 09:43 沐阳 阅读(298) 评论(1) 推荐(0) 编辑
摘要:题意:给定一个矩形,现在给出这个矩形中某些点存在柿子树,问在一个长和宽限定的子矩形内最多有多少个柿子树.解法:由于此题中数据量不大.直接树状数组统计然后暴力.代码如下:#include <cstdlib>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;int N, R, C, dx, dy;int tr[105][105];inline int lowbit(int x) { return x & 阅读全文
posted @ 2013-01-14 15:27 沐阳 阅读(440) 评论(0) 推荐(0) 编辑
摘要:http://www.cnblogs.com/ambition/archive/2011/04/06/bit_rmq.html代码如下:#include<iostream>#include<cstdio>#include<cstdlib>#include<algorithm>#include<cmath>#include<queue>#include<set>#include<map>#include<cstring>#include<vector>#include<s 阅读全文
posted @ 2012-10-06 22:36 沐阳 阅读(1325) 评论(0) 推荐(0) 编辑
摘要:每次计算出当前点与第一个点的距离即可。代码如下;#include<stdio.h>#include<stdlib.h>#include<string.h>#include<iostream>#include<vector>#include<string>#include<math.h>#include<map>#include<set>#include<algorithm>#define MAXN 100000using namespace std;int N, M, cur 阅读全文
posted @ 2012-09-20 21:22 沐阳 阅读(255) 评论(0) 推荐(0) 编辑
摘要:这题是求一个圈中取出若干相邻的数,求其中的最大值,不能够同时取所有的数。本来想着要扩张出一个圈出来,再通过限定长度来转化为线段上的问题,但是这样明显就复杂很多了,有一个结论就是当这个区间在[1-N]之间的话,那么直接输出最大值,否则一定是sum - min[1-N],也就是说如果区间跨越了1,N两个点,那么[1-N]中就一定隐藏了最小子串和。很好理解,因为整个循环串就是由一个最大子串和一个最小子串组成的。线段树的节点中要存储较多的值:lmax -- 从左边开始至少取一个节点的最大值lmin -- 从左边开始至少取一个节点的最小值rmax -- 从右边开始至少取一个节点的最大值rmin -- 从 阅读全文
posted @ 2012-09-20 11:37 沐阳 阅读(237) 评论(0) 推荐(0) 编辑
摘要:通过离线处理,由于线段树不能够动态的扩张,将所有的数都进行永久标号,无视信息的冗余。对于每一个节点,保留对5取余的所有余数的和值,用long long存储。然后根据元素个数进行更新。代码如下:#include<iostream>#include<cstdio>#include<cstdlib>#include<algorithm>#include<cmath>#include<queue>#include<set>#include<map>#include<cstring>#includ 阅读全文
posted @ 2012-09-19 10:22 沐阳 阅读(270) 评论(0) 推荐(0) 编辑
摘要:题意为给定了N棵树,M个蘑菇,一阵风刮来,求期望的幸存的蘑菇的权值。我们可以转化为求每一个蘑菇仔这一阵风过后的期望权值,然后再把所有的蘑菇的权值相加即可。所以我们对于每一棵树进行一次更新,在其安全区域进行更新,这个用线段树来解决,然后再询问一次蘑菇所在地方的安全系数就可以了。代码如下:#include<iostream>#include<cstdio>#include<cstdlib>#include<algorithm>#include<cmath>#include<queue>#include<set>#i 阅读全文
posted @ 2012-08-20 19:16 沐阳 阅读(286) 评论(0) 推荐(0) 编辑
摘要:这题好像是POJ的一道原题... 首先这题我们能够确定如果一条线段被另外一条线段所包含的话,那么那条包含它的线段的左端点一定小于或者等于这个线段。于是我们按照左端点从小到大排序,左端点相同按照右端点从大到小排序,这样就能够保证所有包含第i条线段的线段一定在前面得到了更新。接着我们就直接要求前面线段的右区间大于改线段,由于用的树状数组,所以用一个数减去这个右端点,所以该右端点就越靠近左边,所以也就能使上树状数组了,需要注意的当两条线段的属性完全一样时,我们要直接把前面的答案赋值给后面的线段,然后再去更新。代码如下:#include <cstring>#include <cstd 阅读全文
posted @ 2012-08-17 01:47 沐阳 阅读(294) 评论(0) 推荐(0) 编辑
摘要:首先说明这题不是在线算法,也不能说是离线算法,就是一个预处理能够把所有的结果全部算出来再O(1)的时间得到答案。首先我们要将一这种上下属的关系通过一个dfs转化为一维的关系,相同属性(指为同一个人的下属)的话,那么在物理上就是连续的。这样处理后,我们考虑一个员工能够被替代的话,那么替代他的人的能力值就一定是比他高的,因此我们按照能力值对所有的人进行一次排序,能力值相同的就按照标号从小到大排,这样的话,就能够保证每次询问一个点时,线段树中的所有值都是满足上下属关系的。排好序后,再从左到右扫描一遍,每次先查询如果解雇这个员工,谁将成为他的最佳替代者。代码如下:#include <cstdli 阅读全文
posted @ 2012-08-15 22:31 沐阳 阅读(729) 评论(0) 推荐(0) 编辑
摘要:比赛的时候竟然没有想到这题可以用树状数组过,由于数字的区间比较小,所以直接开设一个树状数组,定义sum(i) 表示小于i的数的总数。那么判定一个数是否有的条件就是计算sum(i+1) == sum(i) 便可以了,查找第K大的数,也就可以直接二分了。代码如下:#include <cstdlib>#include <cstring>#include <cstdio>#include <algorithm>#define MAXN 100000using namespace std;int c[MAXN+5];int lowbit(int x){ r 阅读全文
posted @ 2012-08-12 20:28 沐阳 阅读(300) 评论(0) 推荐(0) 编辑
摘要:这题是要求一段区间内的不重复的数字之和。我们通过对询问区间的右端点进行排序,然后记录每一数字的上一次的出现的位置,由于询问都是不回溯的那么就可以线性的更新了。代码如下:#include <cstdlib>#include <cstdio>#include <cstring>#include <algorithm>#define MAXN 50005using namespace std;typedef long long int Int64;int N, M, seq[MAXN], last[1000005];Int64 ans[200005], 阅读全文
posted @ 2012-08-11 11:13 沐阳 阅读(316) 评论(1) 推荐(0) 编辑
摘要:这题主要是要将两个串的比较进行优化,不能够每次都从起始位置进行匹配。这里用到了线段树进行优化,将每段区间的hash值进行更新和维护,通过找某一点的有连续段最长相同串即可。代码如下:#include <cstdlib>#include <cstring>#include <cstdio>#include <algorithm>#define MOD 100000003#define T 29LLusing namespace std;typedef unsigned int Int64;// 这里建立棵线段树记录某一短区间的hash值 char s 阅读全文
posted @ 2012-08-04 22:17 沐阳 阅读(368) 评论(0) 推荐(0) 编辑
摘要:这题是按照解题报告上的思想来写的。觉得这个想法确实是很麻烦。现大概说下思路:首先我们需要通过dp求出每一个点上下左右的延伸距离,这个距离就是连续的1的个数。预处理好这个信息后。我们不是去枚举每个点,而是去枚举每条对角线上的点,按照上面的说法,一条很明显的规则:所有边由1组成的正方形的对角线一定是矩形的对角线,相比N^2的枚举每个点,这个做法有个好处就是减少重复计算。我们的主要思想还是确定一个点为目标正方形的左上角点,然后取右、下的较小值作为其可及域,我们只需要统计这个区域内的点是否能够能够反向覆盖。对于对角线 L, 我们从左上角到右下角进行遍历, 设当前点的坐标为(i, j),对角线坐标是1( 阅读全文
posted @ 2012-08-03 14:21 沐阳 阅读(361) 评论(0) 推荐(0) 编辑
摘要:该题时间跨度太大,如果直接把时间建树的话显然内存不足,那么我们考虑到只有10^5朵花,于是要采取离散化,但是如果只是离散化花朵的时间的话,那么当我们去查询某个时间的时候,这个时间点在线段树里面的信息的不存在的,于是也就得不到正确的答案,因此我们要离散化花朵和询问的所有时间,这样信息就完备了。代码如下:#include <cstdlib>#include <cstdio>#include <cstring>#include <algorithm>#include <map>using namespace std;int N, M, ti 阅读全文
posted @ 2012-08-01 10:48 沐阳 阅读(233) 评论(0) 推荐(0) 编辑
摘要:该题就是简单的二维树状数组,保留一份棋盘的最新状态即可,树状数组里面就只保留在原有基础上增加或者减少的某一种饺子的数量。代码如下:#include <cstring>#include <cstdlib>#include <cstdio>using namespace std;char op[5];char G[1050][1050];int cc[1050][1050];// 数组中存储韭菜饺的数量,白菜饺的数量通过总数量减去韭菜饺来求void init(){ int k = 0; // 定义韭菜为1,白菜为0 for (int i = 1; i <= 阅读全文
posted @ 2012-08-01 09:08 沐阳 阅读(226) 评论(0) 推荐(0) 编辑
摘要:首先申明此方法POJ超时,HDU压线过,优化版见http://www.cnblogs.com/Lyush/archive/2012/07/28/2613516.html线段树的写法与上面链接中的离散化版本的想法是相近的,只不过这里仅仅是通过线段树来保留某一x区域的多个矩形的面积并。代码如下:#include <cstdlib>#include <cstring>#include <cstdio>#include <map>#include <algorithm>using namespace std;int N, M, Q, cnt; 阅读全文
posted @ 2012-07-30 11:29 沐阳 阅读(355) 评论(0) 推荐(0) 编辑

点击右上角即可分享
微信分享提示