hdu1754 I hate it线段树模板 区间最值查询
2015-10-04 11:54
603 查看
题目链接:这道题是线段树,树状数组最基础的问题
两种分类方式:按照更新对象和查询对象
单点更新,区间查询;
区间更新,单点查询;
按照整体维护的对象:
维护前缀和;
维护区间最值.
复杂度是O(lgn*lgn).这道题数据好像有点弱,因为错误的代码也能过这道题.
根节点(1号结点)和每层的第一个节点和最后一个节点是哨兵节点,永远都不会访问到它们.它们的存在只是方便编程.老子曰:将欲取之,必先予之.一言以蔽之,就是两端边界处的节点都是哨兵节点,这是开区间方便编程导致的.
那么闭区间写法行不行呢?对于长度为N的线段,1~N全部用上,查询时闭区间可以转换成开区间.如果有1023个数,那就只需要开辟1024片叶子的树状数组.开区间=闭区间-两个端点.若无论如何都更新两个端点,那就会导致更新的东西有点靠下,这对于结果并没什么影响.需要注意l==r的情形.
zwk开区间写法
zwk闭区间写法:下面的代码有bug.能过题.
两种分类方式:按照更新对象和查询对象
单点更新,区间查询;
区间更新,单点查询;
按照整体维护的对象:
维护前缀和;
维护区间最值.
线段树模板代码
#include<iostream> #include<string.h> #include<stdio.h> #include<math.h> #include<algorithm> using namespace std; #define re(i,n) for(int i=0;i<n;i++) typedef long long ll; const int maxn = 2 * 1e5 + 7; #define lson(x) x<<1,f,mid #define rson(x) x<<1|1,mid+1,t int n, m; int tr[maxn << 2]; int x, y; int ans; void build(int r, int f, int t){ if (f == t){ scanf("%d", &tr[r]); return; } int mid = (f + t) >> 1; build(lson(r)),build(rson(r)); tr[r] = max(tr[r << 1], tr[r << 1 | 1]); } void query(int r, int f, int t){ if (f >= x&&t <= y){ ans = max(ans, tr[r]); return; } int mid = (f + t) >> 1; if (x <= mid)query(lson(r)); if (y > mid)query(rson(r)); } void update(int r, int f, int t){ if (f == t){ tr[r] = y; return; } int mid = (f + t) >> 1; if (x<= mid)update(lson(r)); else update(rson(r)); tr[r] = max(tr[r << 1], tr[r << 1 | 1]); } int main(){ //freopen("in.txt", "r", stdin); while (cin >> n >> m){ build(1, 1, n); while (m--){ char op[2]; scanf("%s%d%d", op,&x,&y); if (op[0] == 'Q'){ if (x > y)swap(x, y); ans = -1; query(1, 1, n); printf("%d\n", ans); } else{ update(1, 1, n); } } } return 0; }
树状数组区间最值模板代码
#include<iostream> #include<stdio.h> #include<algorithm> using namespace std; const int maxn = 2*1e5+7; typedef long long ll; #define re(i,n) for(int i=0;i<n;i++) int a[maxn], c[maxn]; /* a里面存放本值,c里面维护最值 */ int n, q; int lowbit(int x){ return x&-x; } void redo(int i){ c[i] = i; for (int j = 1; j < lowbit(i); j <<= 1) if (a[c[i - j]]>a[c[i]])c[i] = c[i - j]; } void init(){ for (int i = 1; i <= n; i++){ redo(i); } } void update(int x, int y){ bool big = y > a[x]; a[x] = y; int i = x; if (big){ /* 这个地方如果错写成a[c[i]]<y就会导致无法传递上去,因为一开始时就是a[c[i]]==y. */ while (i<=n&&a[c[i]] <= y)c[i] = x, i += lowbit(i); } else{ while (i<=n&&c[i] == x)redo(i), i += lowbit(i); } } int query(int l, int r){ int ans = a[r]; while (l<=r){ while (r - lowbit(r) >= l){ ans = max(a[c[r]], ans); r -= lowbit(r); } ans = max(a[r], ans);//根节点 r--;//向下走一步 } return ans; } int main(){ freopen("in.txt", "r", stdin); while (scanf("%d%d", &n, &q) == 2){ re(i, n)scanf("%d", &a[i + 1]); init(); int qi = 0; while (q--){ char op[2]; int x, y; qi++; scanf("%s%d%d", &op, &x, &y); if (op[0] == 'U'){ update(x, y); } else{ int ans = query(x, y); printf("%d\n", ans); } } } return 0; }
复杂度是O(lgn*lgn).这道题数据好像有点弱,因为错误的代码也能过这道题.
zwk线段树模板
zwk线段树巧妙之处在于开区间写法,对于长度为N的线段,初始化时,共N+2片叶子:1和N+2空着,2~N+1才是本体,是实实在在的数据.张昆玮说了:如果有1023个数据,那就要有2048片叶子.空间要开到(N+2)*4才行.虽然费了点空间,但编程简洁了.根节点(1号结点)和每层的第一个节点和最后一个节点是哨兵节点,永远都不会访问到它们.它们的存在只是方便编程.老子曰:将欲取之,必先予之.一言以蔽之,就是两端边界处的节点都是哨兵节点,这是开区间方便编程导致的.
那么闭区间写法行不行呢?对于长度为N的线段,1~N全部用上,查询时闭区间可以转换成开区间.如果有1023个数,那就只需要开辟1024片叶子的树状数组.开区间=闭区间-两个端点.若无论如何都更新两个端点,那就会导致更新的东西有点靠下,这对于结果并没什么影响.需要注意l==r的情形.
zwk开区间写法
#include<iostream> #include<stdio.h> #include<algorithm> using namespace std; const int maxn = 2 * 1e5 + 7; typedef long long ll; #define re(i,n) for(int i=0;i<n;i++) int a[maxn << 2]; int n, q, sz; void update(int x, int v){ x += sz; a[x] = v; while (x >1){ x >>= 1; a[x] = max(a[x << 1], a[x << 1 | 1]); } } int query(int l, int r){ l += sz - 1, r += sz + 1; int ans =0; while (l^r ^ 1){//当两人不是兄弟时 if (~l & 1){ ans = max(ans, a[l ^ 1]); } if (r & 1){ ans = max(ans, a[r ^ 1]); } l >>= 1, r >>= 1; } return ans; } int main(){ //freopen("in.txt", "r", stdin); while (scanf("%d%d", &n, &q) == 2){ sz = 1; while (sz < n+2)sz <<= 1; re(i, n){ scanf("%d", &a[i + sz+1]); } for (int i = n; i <= (sz << 1); i++)a[i + sz+1] = 0; for (int i = sz - 1; i>0; i--){ a[i] = max(a[i << 1], a[i << 1 | 1]); } while (q--){ char op[2]; int x, y; scanf("%s%d%d", op, &x, &y); if (op[0] == 'U'){ update(x, y); } else{ int ans = query(x, y); printf("%d\n", ans); } } } return 0; }
zwk闭区间写法:下面的代码有bug.能过题.
#include<iostream> #include<stdio.h> #include<algorithm> using namespace std; const int maxn = 2 * 1e5 + 7; typedef long long ll; #define re(i,n) for(int i=0;i<n;i++) int a[maxn << 2]; int n, q, sz; void update(int x, int v){ x += sz-1; a[x] = v; while (x >1){ x >>= 1; a[x] = max(a[x << 1], a[x << 1 | 1]); } } int query(int l, int r){ l += sz - 1, r += sz - 1; int ans =max(a[l],a[r]); //查询时先把两个端点给处理掉,就相当于开区间了. /* 这个地方的不同决定了两种写法,我觉得没有必要开区间.闭区间预处理一下就很好. 这个地方如果l==r,就会产生死循环.但是我这么写,这道题却过了. */ while (l^r ^ 1){//当两人不是兄弟时 if (~l & 1){ ans = max(ans, a[l ^ 1]); } if (r & 1){ ans = max(ans, a[r ^ 1]); } l >>= 1, r >>= 1; } return ans; } void init(){ sz = 1; while (sz < n)sz <<= 1; re(i, n){ scanf("%d", &a[i + sz]);//在1~N之间存放数据 } for (int i = n; i <= (sz << 1); i++)a[i + sz ] = 0; for (int i = sz - 1; i>0; i--){ a[i] = max(a[i << 1], a[i << 1 | 1]); } } int main(){ freopen("in.txt", "r", stdin); while (scanf("%d%d", &n, &q) == 2){ init(); while (q--){ char op[2]; int x, y; scanf("%s%d%d", op, &x, &y); if (op[0] == 'U'){ update(x, y); } else{ int ans = query(x, y); printf("%d\n", ans); } } } return 0; }
相关文章推荐
- 证券基础--股票发行方式
- Odoo9发行说明
- 软件工程之面向过程的软件设计方法(二)
- J2EE学习路线
- 深入详解保护模式下的内存分页机制
- 证书
- andriod 实现新浪、QQ场地、朋友微信圈、微信朋友分享功能
- DirectX11 雾
- Hbase笔记二:简明系统架构
- 获取函数或程序相关的源码
- Matrix学习
- 经典算法题——第十三题 赫夫曼树
- MyEclipse 2014 破解图文详细教程
- Hbase笔记一:了解Hbase
- Java中有关Null的9件事
- 96. Unique Binary Search Trees (Tree; DFS)
- Asp.Net MVC 使用 DataAnnotations 进行模型验证
- 错误 10 非静态的字段、方法或属性“Test10.Program.a”要求对象引用
- HDU 1015 爆搜 /dfs+回溯
- 经典算法题——第十二题 线段树