随笔分类 -  树状数组

摘要:把数值和查询放在一起从小到大排序,纪录每个数值的位置,当遇到数值时就更新到树状数组中,遇到查询就直接查询该区间和。#include #include #include #include using namespace std;const int MAXN = 200100;struct node{ int id; int L, R; int val;} qq[MAXN];int N, Q;int cntQ;int ans[MAXN/2];int C[MAXN];bool cmp( const node& a, const node& b ){ if ( a.val ==... 阅读全文
posted @ 2013-10-11 22:51 冰鸮 阅读(245) 评论(0) 推荐(0)
摘要:水题,练习一下树状数组实现区间更新。对于每个区间,区间左端点+1,右端点的后一位-1,查询每个位置的覆盖次数#include #include const int MAXN = 100005;int N, C[MAXN];int lowbit( int x ){ return (-x)&x;}void update(int x, int add){ for(int i = x; i 0; i -= lowbit(i)) ret += C[i]; return ret;}int main(){ while( scanf("%d", &N)== 1 && 阅读全文
posted @ 2013-10-10 20:07 冰鸮 阅读(228) 评论(0) 推荐(0)
摘要:将每个球按输入顺序编号,建立 它第几个被扔掉->编号 的映射关系。记录当前在手里的球的编号,按扔掉的顺序查找这个球的编号,看看这个球是逆时针转到手里更近还是顺时针转到手里更近,即当前扔掉球的编号与当前手里球的编号之间有几个球。树状数组C[i]记录编号i的球是否还在。球是环形排列的,特殊处理一下。对于扔掉一个球之后下一个落在手里的球的编号,二分判定,找顺时针方向第一个有球的位置#include #include #include #include #define LL long long intusing namespace std;const int MAXN = 100100;int 阅读全文
posted @ 2013-09-02 22:24 冰鸮 阅读(220) 评论(0) 推荐(0)
摘要:一组数据:29 10 22 3 1 4 2 5 1 3 40 1 0 1 1 1 1 1 010 410 410 50 710 215 10 21 1 1 1 10 1 0 1 010 310 310 10 40 50 21答案:16161616156666以第一组为例:2 3 1 4 2 5 1 3 4以小于K的数为分界,将数列分成几段。对于每个数字,记录它所在段的左端点和右端点,据此求出修改前的合法对数sum。对于每个修改,查看当前修改发生在哪一段,该修改对sum产生了怎样的影响,修改sum即可。树状数组C[i]记录区间[1, i]共有多少个白点。注意修改发生在段内和段端点处要分开考虑。# 阅读全文
posted @ 2013-08-30 21:03 冰鸮 阅读(264) 评论(0) 推荐(0)
摘要:对这棵树DFS遍历一遍,同一节点入栈和出栈之间访问的节点就是这个节点的子树。因此节点入栈时求一次 小于 i 的节点个数 和,出栈时求一次小于 i 的节点个数和,两次之差就是答案。PS.这题直接DFS会爆栈,可以重新设置栈的大小#pragma comment(linker,"/STACK:100000000,100000000")也可以人工模拟栈,代码如下。#include #include #include #include using namespace std;const int MAXN = 110000;struct Edge{ int v; int next;}; 阅读全文
posted @ 2013-08-21 10:26 冰鸮 阅读(268) 评论(0) 推荐(0)
摘要:原文地址:http://www.cppblog.com/MatoNo1/archive/2011/03/19/142226.html树状数组在区间求和问题上有大用,其三种复杂度都比线段树要低很多……有关区间求和的问题主要有以下三个模型(以下设A[1..N]为一个长为N的序列,初始值为全0):(1)“改点求段”型,即对于序列A有以下操作:【1】修改操作:将A[x]的值加上c;【2】求和操作:求此时A[l..r]的和。这是最容易的模型,不需要任何辅助数组。树状数组中从x开始不断减lowbit(x)(即x&(-x))可以得到整个[1..x]的和,而从x开始不断加lowbit(x)则可以得到x 阅读全文
posted @ 2013-08-20 20:11 冰鸮 阅读(199) 评论(0) 推荐(0)
摘要:题意:求区间内不同的数的和离线处理,按查询右端点从小到大排序,从左往右扫一遍。记录每个数出现的上一个位置,如果该数之前没有出现过,就加上,否则就在上一个位置减去。#include #include #include #include #define LL long long intusing namespace std;const int MAXN = 1000100;struct node{ int id; int l, r;};int N, Q;int pre[MAXN]; //某数之前出现的位置LL sum[50100];LL val[50100];int maxL... 阅读全文
posted @ 2013-08-20 20:03 冰鸮 阅读(173) 评论(0) 推荐(0)
摘要:虽然看起来是求最短路,但因为条件的限制,可以转化为区间求最小值。对于一条small path [a, b],假设它的长度是len,它对区间[a, b]的影响就是:len-( sum[b]-sum[a-1] );(使区间[a,b]的原有长度变长或者变短,变长没有意义,所以我们只考虑变短的情况),因为只能选择一条small path,所以对于每个查询[u, v],就是要选择在区间[u, v]内,让原有长度减少最多的那条small path,即求区间最小值。离线处理:将查询和small path放在一起排序,按u从大到小,v从小到大排。因为我们要从后往前扫,对于每个查询[u, v],我们应当保证在本 阅读全文
posted @ 2013-08-19 22:04 冰鸮 阅读(314) 评论(0) 推荐(0)
摘要:给你N的一个排列,求满足:a[i] 2 #include 3 #include 4 #include 5 6 #define LL long long int 7 8 using namespace std; 9 10 const int MAXN = 100100;11 const LL MOD = 100000007;12 13 int N;14 int C[MAXN];15 int num[MAXN];16 int low[MAXN];17 int high[MAXN];18 19 int lowbit( int x )20 {21 return x & ( -x )... 阅读全文
posted @ 2013-08-04 14:41 冰鸮 阅读(211) 评论(0) 推荐(0)
摘要:一共最多才100000个数,并且数值范围0~100000。树状数组 C[i] 记录数值为 i 的数有多少个。删除时如果Query( a ) - Query( a - 1 ) == 0 则该数不存在。求大于a的第K大数只需要对大于a的数二分查找一下,Query( MAXN ) - Query(a)为大于 a 的数的总个数,如果小于K 则不存在。 1 #include 2 #include 3 #include 4 #include 5 6 using namespace std; 7 8 const int MAXN = 100010; 9 10 int Q;11 int C[MAX... 阅读全文
posted @ 2013-08-03 20:37 冰鸮 阅读(149) 评论(0) 推荐(0)
摘要:实际上就是问这个区间编号连续的段的个数,假如一个编号连续的段有(a+b)个人,我把他们分在同一组能得到的分值为(a+b)^2,而把他们分成人数为a和b的两组的话,得到的分值就是a^2+b^2,显然(a+b)^2 > a^2+b^2,所以对于每个区间,尽可能把他们分成尽量少的组。考虑把这些数从后往前添加,每添加一个数num[i],如果num[i]+1或者num[i]-1有且只有一个已经存在,则段数不变;如果num[i]+1和num[i]-1同时存在,则段数-1,如果都不在,则段数+1。对于每个数num[i],我们记录把它添加进去的时候,它对段数产生的影响为c[i]( 即插入num[i]时对 阅读全文
posted @ 2013-08-02 21:09 冰鸮 阅读(219) 评论(0) 推荐(0)
摘要:思路参考这里。 1 #include 2 #include 3 #include 4 #include 5 6 using namespace std; 7 8 const int MAXN = 50010; 9 10 struct node 11 { 12 int l, r; 13 int idx; 14 }; 15 16 node Qry[MAXN]; //查询 17 int C[MAXN]; //树状数组 18 int vis[MAXN]; // i 的倍数上一次出现的位置 19 int num[MAXN]; //原数组... 阅读全文
posted @ 2013-07-31 15:01 冰鸮 阅读(251) 评论(0) 推荐(0)
摘要:先提个注意点,由于Lowbit(0) = 0,这会导致x递增的那条路径发生死循环,所有当树状数组中可能出现0时,我们都全部加一,这样可以避免0带来的麻烦~~简单: POJ 2299 Ultra-QuickSorthttp://acm.pku.edu.cn/JudgeOnline/problem?id=2299 求逆序数,可以用经典的归并排序做,也是基本的树状数组题目。 POJ 2352 Stars http://acm.pku.edu.cn/JudgeOnline/problem?id=2352 题目意思就是求每个星星左下方的星星的个数,由于y轴已经排序好了,我们可以直接用按x轴建立一维树状数 阅读全文
posted @ 2013-07-29 20:02 冰鸮 阅读(875) 评论(0) 推荐(0)
摘要:题意:设原数组为a[i],pos[i]代表第 i 个位置之前有多少个数比a[i]大,求原数组a[i]。这个题意是看了别人的题解才明白,我自己没读出来……方法:假设我们从左往右放,因为后面的数还有可能影响前面的数的位置,所以在最后一个数放完之前,我们没法确定每个数的位置,所以我们反过来考虑,从右往左放。因为每个数前面比它大的数的个数pos[i]已知,我们可以不必关心这些数的具体数值,从而转化为它从右往左走了多少个空格,即pos[i]个,因此这个数放在第 pos[i] + 1 个空格位置上。这个空格所在位置的下标id,即是a[i]。a[i] = id;树状数组或线段树记录区间[1, i ]的空格个 阅读全文
posted @ 2013-07-29 19:38 冰鸮 阅读(298) 评论(0) 推荐(0)
摘要:题意:给你一个序列,问相邻两数高度差绝对值小于等于H的子序列有多少个。dp[i]表示以i为结尾的子序列有多少,易知状态转移方程为:dp[i] = sum( dp[j] ) + 1;( abs( height[i] - height[j] ) 2 #include 3 #include 4 #include 5 6 using namespace std; 7 8 const int MAXN = 100100; 9 const int MOD = 9901;10 11 int dp[MAXN];12 int height[MAXN];13 int num[MAXN];14 int ... 阅读全文
posted @ 2013-07-18 12:36 冰鸮 阅读(314) 评论(0) 推荐(0)
摘要:研究了整整一天orz……直接上官方题解神思路 1 #include 2 #include 3 #include 4 #include 5 #include 6 7 using namespace std; 8 9 const int MAXN = 100100; 10 11 struct node 12 { 13 int v, next; 14 }; 15 16 struct subTree 17 { 18 int st, ed; 19 }; 20 21 struct Queryy 22 { 23 int i; 24 int... 阅读全文
posted @ 2013-07-16 15:40 冰鸮 阅读(253) 评论(0) 推荐(0)
摘要:树状数组+扫描线官方题解:本题题目大意在一个01方阵中找出四条边全都是1的正方形的个数,对于正方形内部则没有要求。 一个直观的想法是首先用N^2的时间预处理出每一个是1的点向上下左右四个方向能够延伸的1的最大长度,记为四个数组l, r, u, d。然后我们观察到正方形有一个特征是同一对角线上的两个顶点在原方阵的同一条对角线上。于是我们可以想到枚举原来方阵的每条对角线,然后我们对于每条对角线枚举对角线上所有是1的点i,那么我们可以发现可能和i构成正方形的点应该在该对角线的 [i, i + min(r[i], d[i]) – 1] 闭区间内, 而在这个区间内的点 j 只要满足 j – i + 1 阅读全文
posted @ 2013-05-08 23:36 冰鸮 阅读(188) 评论(0) 推荐(0)
摘要:题目链接:http://poj.org/problem?id=1195二维树状数组的应用 1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 5 const int MAXN = 1200; 6 int S; 7 int C[MAXN][MAXN]; 8 9 int lowbit( int x )10 {11 return x & -x;12 }13 14 void Add( int x, int y, int add )15 {16 int tpy;17 while ( x 阅读全文
posted @ 2013-04-15 21:05 冰鸮 阅读(204) 评论(0) 推荐(0)
摘要:题目链接:http://poj.org/problem?id=3321刷POJ分类的时候遇到的题,只知道是树状数组,但是分支编号不连续,不晓得怎么用树状数组求和。后来看了解题报告才知道需要把分支通过DFS映射成编号连续的,记录每个子树的起始位置和终止位置,然后再用树状数组求和。即使明白了思路,代码实现上也还是有很多地方不理解。唉,数据结构学得实在有够糟糕……树的邻接表表示法每次往表头插入节点也让我费解了好半天Orz…… 1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 5 cons 阅读全文
posted @ 2013-04-15 20:42 冰鸮 阅读(158) 评论(0) 推荐(0)