题目链接:http://soj.me/1136
题目的问题是频繁的区间查询,对于频繁的区间查询问题,很明显不能暴力地每次遍历了,一般需要经过预处理然后通过二分或者线段树的方式来查找(其实都是二分的思想)。
线段树的典型用法是频繁的更新一段区间,查询一段区间,每次都是O(lgN)的复杂度,那么总体复杂度就是O(NlgN)。
本题的应用场景虽然没有频繁的更新区间,不过也适合线段树的使用场景(一般的二分查找搞不定了!!),不过线段树的难点有两个,一是确定节点需要保存信息,二是怎么根据子节点的信息来更新父节点的信息。
因为该题查找的是给定区间[a,b]之内的喜恶度之和最大的一个连续的子区间[i, j],所以可以如下来设计节点:
lmax---表示一个节点(线段)从左边起的最大连续喜恶度之和;
rmax---表示一个节点(线段)从右边起的最大连续喜恶度之和;
max----表示一个节点(线段)整体的最大连续喜恶度之和(可能是lmax或rmax,也可能是中间的某个子线段);
sum----表示一个节点(线段)中所有的数字之和。
因为题目中还需要求得下标,所以还需要如下信息:
left、right----该节点本身所表示的下标范围(几乎线段树的节点都会保存的);
lright---该节点从左边起达到lmax的下标;
rleft-----该节点从右边起达到rmax的下标;
mleft、mright-----该节点取得max的左右下标;
有了这些节点信息之后,建树的时候,由子节点的信息来更新父节点的信息就容易了:
父节点的lmax等于MAX(左子节点的lmax, 左子节点的和+右子节点的lmax);
父节点的rmax等于MAX(右子节点的rmax, 右子节点的和+左子节点的rmax);
父节点的max等于MAX(左子节点的max, 右子节点的max, 左子节点的rmax+右子节点的lmax);
父节点的sum等于左子节点的sum + 右子节点的sum;
以上下标跟着更新。
可是这个题建好了树之后还并没有完,查询还是一个比较麻烦的事,当需要查询的区间完全落在左右两边的时候,直接返回,可是如果区间[a, b]被分为了两段了该怎么办呢?这里想了很久,突然灵机一动想到了构造线段,也就是query函数返回的是一条线段(一个节点),而这条线段的构造方式和建树时的构造方式相同,同样需要维护lmax, rmax, max, sum,只不过它的left和right是你自己想构造的线段的左右下标,这样问题就解决了。
#include <cstdio> const int MAX = 100005; //节点需要维护的信息 struct Node { int left, right, rleft, lright, mleft, mright; int max, lmax, rmax, sum; }treeNode[4*MAX]; int a[MAX]; void build(int left, int right, int root) { if(left == right) { treeNode[root].left = left, treeNode[root].right = right; treeNode[root].rleft = left, treeNode[root].lright = right; treeNode[root].mleft = left, treeNode[root].mright = right; treeNode[root].lmax= a[left], treeNode[root].rmax = a[right]; treeNode[root].max = a[left], treeNode[root].sum = a[left]; return; } int mid = (left + right)>>1; build(left, mid, root<<1); build(mid+1, right, (root<<1) | 1); //确定left/right treeNode[root].left = left, treeNode[root].right = right; //确定lmax及其下标 treeNode[root].lmax = treeNode[root<<1].lmax; treeNode[root].lright = treeNode[root<<1].lright; if(treeNode[root<<1].sum + treeNode[(root<<1) | 1].lmax > treeNode[root].lmax) { treeNode[root].lmax = treeNode[root<<1].sum + treeNode[(root<<1) | 1].lmax; treeNode[root].lright = treeNode[(root<<1) | 1].lright; } //确定rmax及其下标 treeNode[root].rmax = treeNode[(root<<1) | 1].rmax; treeNode[root].rleft = treeNode[(root<<1) | 1].rleft; if(treeNode[(root<<1) | 1].sum + treeNode[root<<1].rmax >= treeNode[root].rmax) { treeNode[root].rmax = treeNode[(root<<1) | 1].sum + treeNode[root<<1].rmax; treeNode[root].rleft = treeNode[root<<1].rleft; } //确定sum treeNode[root].sum = treeNode[root<<1].sum + treeNode[(root<<1) | 1].sum; //确定max treeNode[root].max = treeNode[root<<1].max; treeNode[root].mleft = treeNode[root<<1].mleft; treeNode[root].mright = treeNode[root<<1].mright; if(treeNode[root<<1].rmax + treeNode[(root<<1) | 1].lmax > treeNode[root].max) { treeNode[root].max = treeNode[root<<1].rmax + treeNode[(root<<1) | 1].lmax; treeNode[root].mleft = treeNode[root<<1].rleft; treeNode[root].mright = treeNode[(root<<1) | 1].lright; } if(treeNode[(root<<1) | 1].max > treeNode[root].max) { treeNode[root].max = treeNode[(root<<1) | 1].max; treeNode[root].mleft = treeNode[(root<<1) | 1].mleft; treeNode[root].mright = treeNode[(root<<1) | 1].mright; } } //查询返回一条线段 Node query(int a, int b, int root) { if(treeNode[root].left == a && treeNode[root].right == b) return treeNode[root]; int mid = (treeNode[root].left + treeNode[root].right) >> 1; if(mid >= b) return query(a, b, root<<1); else if(a > mid) return query(a, b, (root<<1) | 1); else { //通过线段ans1和ans2来构造新的线段ans,构造方式同建树 Node ans1, ans2, ans; ans1 = query(a, mid, root<<1); ans2 = query(mid+1, b, (root<<1) | 1); ans.left = ans1.left, ans.right = ans2.right; ans.lmax = ans1.lmax, ans.lright = ans1.lright; if(ans1.sum + ans2.lmax > ans.lmax) ans.lmax = ans1.sum + ans2.lmax, ans.lright = ans2.lright; ans.rmax = ans2.rmax, ans.rleft = ans2.rleft; if(ans2.sum + ans1.rmax >= ans.rmax) ans.rmax = ans2.sum + ans1.rmax, ans.rleft = ans1.rleft; ans.sum = ans1.sum + ans2.sum; ans.max = ans1.max, ans.mleft = ans1.mleft, ans.mright = ans1.mright; if(ans1.rmax + ans2.lmax > ans.max) { ans.max = ans1.rmax + ans2.lmax; ans.mleft = ans1.rleft, ans.mright = ans2.lright; } if(ans2.max > ans.max) ans.max = ans2.max, ans.mleft = ans2.mleft, ans.mright = ans2.mright; return ans; } } int main() { int n, m, x, y; Node ans; scanf("%d %d", &n, &m); for(int i=1; i<=n; i++) scanf("%d", &a[i]); build(1, n, 1); while(m--) { scanf("%d %d", &x, &y); ans = query(x, y, 1); printf("%d %d %d\n", ans.mleft, ans.mright, ans.max); } return 0; }