sicily 1136 山海经【线段树】

题目链接: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;
}


你可能感兴趣的:(sicily 1136 山海经【线段树】)