SPOJ 1043 Can you answer these queries I (超强线段树)
来源:互联网 发布:mac 中文字体 ttf 编辑:程序博客网 时间:2024/06/05 11:23
GSS1 - Can you answer these queries I
#tree
You are given a sequence A[1], A[2], ..., A[N] . ( |A[i]| ≤ 15007 , 1 ≤ N ≤ 50000 ). A query is defined as follows:
Query(x,y) = Max { a[i]+a[i+1]+...+a[j] ; x ≤ i ≤ j ≤ y }.
Given M queries, your program must output the results of these queries.
Input
- The first line of the input file contains the integer N.
- In the second line, N numbers follow.
- The third line contains the integer M.
- M lines follow, where line i contains 2 numbers xi and yi.
Output
- Your program should output the results of the M queries, one query per line.
Example
Input:3 -1 2 311 2Output:2
题意简单明了,静态求区间最大连续子序列。
想起之前做过的一道LCT的题目,是求链上染色区域个数,和这个有丝丝相似。
首先,通过观察区间最大连续子序列和的构成,我们发现,某一个区间的最大连续子序列max的备选序列可以是它的左右子区间的最大连续子序列,或者是左子区间的最大右端连续子序列rmax与右子区间的最大左端连续子序列lmax的和。那么在一棵线段树中,我们需要维护的就是该区间的最大连续子序列max、最大左端连续子序列lmax和最大右端连续子序列rmax。
维护max,刚刚已经说了,那么lmax和rmax如何维护呢。这个我也是错了几次后才弄懂的。首先,显然的lmax可以是继承它左子区间的lmax,但是如果说右子区间的lmax很大,那会不会影响总区间的lmax呢?答案是肯定的。对于lmax,它的大小是左子区间的lmax 与 左子区间和+右子区间lmax 中大的那个。即左端最大连续子区间可以是左子区间的lmax,也可以是整个左子区间的和加上有自取件的lmax。简单的例子就是:-1,2。区间[1,1]的lmax=-1,则区间[1,2]的lmax应该等于2-1=1,而不是-1。同理,右端连续最大也是这样维护的。这也就意味着我们还有维护一个区间和sum,这个就是非常基础的线段树操作了,在这里不再赘述。
还有一个比较麻烦的地方就是查询了。即使写好了维护,查询也不是那么好理解的。因为查询的区间可能横跨两个子区间,这个时候又要仔细考虑了。同样,它可以是两个子区间中max大的那个,还可以是左子区间的rmax+右子区间的lmax,而要求这个lmax和rmax,则又要写两个函数去计算。然后在getlmax和getrmax的时候,要点和维护lmax和rmax的时候一样,返回的最优解可能也会跨一个子区间,所以说我们还要写一个getsum的函数计算区间总和。真够麻烦的,具体见代码:
#include<bits/stdc++.h>#define LL long long#define N 201000using namespace std;struct ST{LL a[N];struct node{LL lmax,rmax,max,sum,num;int l,r;} tree[N*4];inline void push_up(int i){ tree[i].sum=tree[i<<1].sum+tree[i<<1|1].sum; tree[i].max=max(tree[i<<1].max,tree[i<<1|1].max);//max是三个备选解中大的那个tree[i].max=max(tree[i].max,tree[i<<1].rmax+tree[i<<1|1].lmax); tree[i].lmax=max(tree[i<<1].lmax,tree[i<<1].sum+tree[i<<1|1].lmax);//lmax、rmax要跨区间比较 tree[i].rmax=max(tree[i<<1|1].rmax,tree[i<<1|1].sum+tree[i<<1].rmax);}inline void build(int i,int l,int r){tree[i].r=r;tree[i].l=l;if (l==r){tree[i].num=tree[i].rmax=a[l];tree[i].lmax=tree[i].max=a[l];tree[i].sum=a[l];return;}LL mid=(l+r)>>1;build(i<<1,l,mid);build(i<<1|1,mid+1,r);push_up(i);} inline LL getsum(int i,int l,int r)//计算区间和{if ((tree[i].l==l)&&(tree[i].r==r)) return tree[i].sum;LL mid=(tree[i].l+tree[i].r)>>1;if (mid>=r) return getsum(i<<1,l,r);else if (mid<l) return getsum(i<<1|1,l,r);else return getsum(i<<1,l,mid)+getsum(i<<1|1,mid+1,r);} inline LL getrmax(int i,int l,int r)//计算右端最大连续子序列{if ((tree[i].l==l)&&(tree[i].r==r)) return tree[i].rmax;LL mid=(tree[i].l+tree[i].r)>>1;if (mid>=r) return getrmax(i<<1,l,r);else if (mid<l) return getrmax(i<<1|1,l,r);else return max(getrmax(i<<1|1,mid+1,r),getrmax(i<<1,l,mid)+getsum(i<<1|1,mid+1,r));//解可能跨区间} inline LL getlmax(int i,int l,int r)//计算左端最大连续子序列{if ((tree[i].l==l)&&(tree[i].r==r)) return tree[i].lmax;LL mid=(tree[i].l+tree[i].r)>>1;if (mid>=r) return getlmax(i<<1,l,r);else if (mid<l) return getlmax(i<<1|1,l,r);else return max(getlmax(i<<1,l,mid),getlmax(i<<1|1,mid+1,r)+getsum(i<<1,l,mid));//解可能跨区间}inline LL getmax(int i,int l,int r)//计算区间最大子序列{if ((tree[i].l==l)&&(tree[i].r==r)) return tree[i].max;LL mid=(tree[i].l+tree[i].r)>>1;if (mid>=r) return getmax(i<<1,l,r);else if (mid<l) return getmax(i<<1|1,l,r);else { LL a=max(getmax(i<<1,l,mid),getmax(i<<1|1,mid+1,r)); LL b=getrmax(i<<1,l,mid)+getlmax(i<<1|1,mid+1,r); return max(a,b);//解可能是两个子区间值大的那个也可能是中间部分 }}} seg;int n;int main(){ while(~scanf("%d",&n)) { memset(seg.a,0,sizeof(seg.a)); for(int i=1;i<=n;i++) scanf("%lld",&seg.a[i]); seg.build(1,1,n); int q; cin>>q; while(q--) { int x,y; scanf("%d%d",&x,&y); printf("%lld\n",seg.getmax(1,x,y)); } } return 0;}
阅读全文
0 0
- SPOJ 1043 Can you answer these queries I (超强线段树)
- 【SPOJ】 1043 Can you answer these queries I 线段树
- spoj 1043. Can you answer these queries I (线段树)
- 【SPOJ】Can you answer these queries I【线段树】
- [SPOJ GSS1] Can you answer these queries I [线段树]
- SPOJ 1043 Can you answer these queries I(GSS1 线段树)
- 【线段树】 SPOJ 1043 Can you answer these queries I 区间合并
- spoj Can you answer these queries I(线段树 单点更新 区间查询)
- spoj SPOJ 1043 Can you answer these queries I
- SPOJ/GSS3:Can you answer these queries III(线段树)
- spoj 1557. Can you answer these queries II(线段树)
- spoj 1716. Can you answer these queries III(线段树)
- spoj 2713. Can you answer these queries IV(线段树)
- spoj 2916. Can you answer these queries V(线段树)
- SPOJ GSS3 Can you answer these queries III (线段树)
- SPOJ GSS4 Can you answer these queries IV (线段树)
- 【SPOJ】Can you answer these queries III【线段树】
- [SPOJ GSS2] Can you answer these queries II [线段树]
- MSCOMM二进制模式读取内容(中英混合)
- 魔法宝石(类似dp 杭电排位赛-2)
- list_head 双向循环链表的结构
- Call to undefined function openssl_encrypt(),安装laravel报错问题
- 简谈JS的原型链和作用域链
- SPOJ 1043 Can you answer these queries I (超强线段树)
- 什么叫区块链分叉?
- Android 第三方初始化不适合放在SplashActivity
- matlab工具箱分类
- hlist_head hlist_node (哈希链表)
- Codeforces 401D Roman and Numbers 状压DP
- 94. Binary Tree Inorder Traversal
- HashMap与HashTable的区别
- springmvc报错 org.springframework.web.servlet.DispatcherServlet