POJ - 4047 Garden(线段树成段更新,查询最值)

题意:含n个元素的序列 ,有q个操作。

操作(  p x y)有三种:

0 x y :把第x个的值改为y 

1 x y:交换第x个和第y个的值  

2 x y:问区间[x,y]里面连续k个的子序列的最大和


思路:先处理序列,SumK[i]表示从i开始的k个数的和,然后线段树建树,成段更新,查询。更新只要处理成对区间最值得加减就可以了。


成段更新还是不熟悉,于是顺便做了 POJ - 3468 A Simple Problem with Integers,也是成段更新,查询为区间元素和,有些不同。


#include <iostream>
#include <cstdio>
using namespace std;

#define max(a,b) a>b?a:b
const int MAXN = 200000 + 1000;

int num[MAXN];
int sumK[MAXN];
int sum[MAXN];
int father[MAXN];

struct node
{
	int l, r;
	int Max;
	int lazy;
}tree[MAXN*3];

inline void pushUp(int i)
{
	int ls = i << 1, rs = ls + 1;
	tree[i].Max = max(tree[ls].Max, tree[rs].Max);
}

void pushDown(int i)
{
	if (tree[i].lazy)
	{
		int ls = i << 1, rs = ls + 1;
		tree[ls].lazy += tree[i].lazy;
		tree[rs].lazy += tree[i].lazy;
		tree[ls].Max += tree[i].lazy;
		tree[rs].Max += tree[i].lazy;
		tree[i].lazy = 0;
	}
}

void build(int l, int r, int i)
{
	tree[i].lazy = 0;
	tree[i].l = l;
	tree[i].r = r;
	if (l == r)
	{
		tree[i].Max = sumK[l];
		father[l] = i;
		return;
	}
	int ls = i << 1, rs = ls + 1;
	int m = (l + r) >> 1;
	build(l, m, ls);
	build(m + 1, r, rs);
	pushUp(i);
}

void update(int l, int r, int i, int v)
{
	if (l <= tree[i].l && r >= tree[i].r)
	{
		tree[i].lazy += v;
		tree[i].Max += v;
		return;
	}
	
	pushDown(i);

	int m = (tree[i].l + tree[i].r) >> 1, ls = i << 1, rs = ls + 1;
	
	if (r <= m) update(l, r, ls, v);
	else if (l > m) update(l, r , rs, v);
	else
	{
		update(l, m, ls, v);
		update(m + 1, r, rs, v);
	}
	pushUp(i);
}

int query(int l, int r, int i)
{
	if (l<=tree[i].l && r>=tree[i].r)
		return tree[i].Max;
	pushDown(i);
	int m = (tree[i].l + tree[i].r) >> 1, ls = i << 1, rs = ls + 1;
	if (r <= m)
		return query(l, r, ls);
	else if (l > m)
		return query(l, r, rs);
	else
		return max(query(l, m, ls), query(m + 1, r, rs));
}

int main()
{
	int casen;
	scanf("%d", &casen);
	while (casen--)
	{
		int n, m, k;
		scanf("%d%d%d", &n, &m, &k);
		for (int i = 1; i <= n; i++)
			scanf("%d", &num[i]);
		int len = n - k + 1;
		sum[0] = 0;
		for (int i = 1; i <= n; i++)
			sum[i] = sum[i - 1] + num[i];
		for (int i = 1; i <= len; i++)
			sumK[i] = sum[i + k - 1] - sum[i - 1];

		build(1, len, 1);

		int op, x, y;
		while (m--)
		{
			scanf("%d%d%d", &op, &x, &y);
			if (op == 0)
			{
				int add = y - num[x];
				num[x] = y;
				int s = max(x - k + 1, 1);
				int e = x;
				update(s, e, 1, add);
			}
			else if (op == 1)
			{
				int add1 = num[x] - num[y];
				int add2 = num[y] - num[x];
				int s1 = max(x - k + 1, 1);
				int s2 = max(y - k + 1, 1);
				int e1 = x;
				int e2 = y;
			
				int temp = num[x];
				num[x] = num[y];
				num[y] = temp;
				
				update(s1, e1, 1, add2);
				update(s2, e2, 1, add1);
	}
			else
			{
				int s = x;
				int e = max(y - k + 1, 1);
				int ans = query(s, e, 1);
				printf("%d\n", ans);
			}
		}
	}
}


你可能感兴趣的:(线段树,ACM,成段更新)