Poj 4047 Garden /2012金华邀请赛D题(线段树)

题意:给出长度为200000的数列,进行200000次下列操作:

0 x y 将x的值置为y

1 x y 交换x和y的值

2 x y 求[x,y]区间内和最大的一个连续的长度为k的区间(y-x>k)

解法:显而易见的线段树开始一直想维护每个区间的最大k区间,但是一直想不出怎么维护父节点和子节点的关系。估计如果实在现场会困死在此题。。。

       于是需要转换一下思维,把点当做区间把区间当做一个点,具体来说,线段树上每个叶子节点i的值为数列i~i+k-1的和,这样求[x,y]的最大值就转换成了求[x,y-k+1]区间的最大值,对于每次对于点的更新,更新它能影响到的所有长为k的区间,即 [x - k + 1,x];这种思想经常用到,比如前几天碰到的一道alibaba校园赛费用流问题(hdu4106)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
public class Garden {
	class SegTree {
		class node {
			int left, right;
			int max, add;
			int mid() {
				return (left + right) >> 1;
			}
		}
		node tree[]=new node[600010];
		void init(int left, int right, int idx, int a[]) {
			tree[idx] = new node();
			tree[idx].left = left;
			tree[idx].right = right;
			if (left == right) {
				tree[idx].max = a[left];
				return;
			}
			int mid = tree[idx].mid();
			init(left, mid, idx << 1, a);
			init(mid + 1, right, (idx << 1) | 1, a);
			tree[idx].max = Math.max(tree[idx << 1].max,
					tree[(idx << 1) | 1].max);
		}

		void pushdown(int idx) {
			if (tree[idx].add == 0)
				return;
			int k = tree[idx].add;
			tree[idx].max += k;
			tree[idx].add = 0;
			if (tree[idx].left == tree[idx].right)
				return;
			tree[idx << 1].add += k;
			tree[(idx << 1) | 1].add += k;
		}

		void update(int left, int right, int idx, int v) {
			pushdown(idx);
			if (left <= tree[idx].left && right >= tree[idx].right) {
				tree[idx].add += v;
				return;
			}
			int mid = tree[idx].mid();
			if (left <= mid)
				update(left, right, idx << 1, v);
			if (right > mid)
				update(left, right, (idx << 1)|1, v);
			pushdown(idx<<1);
			pushdown((idx<<1)|1);
			tree[idx].max = Math.max(tree[idx << 1].max,
					tree[(idx << 1)|1].max);
		}

		int query(int left, int right, int idx) {
			pushdown(idx);
			if (tree[idx].left == left && tree[idx].right == right) {
				return tree[idx].max;
			}
			int mid = tree[idx].mid();
			if (right <= mid)
				return query(left, right, idx << 1);
			else if (left > mid)
				return query(left, right, (idx << 1) | 1);
			else
				return Math.max(query(left, mid, idx << 1),
						query(mid + 1, right, (idx << 1) | 1));
		}
	}

	SegTree st = new SegTree();
	int sum[]=new int[200010], arr[]=new int[200010], p[]=new int[200010];
	StreamTokenizer in = new StreamTokenizer(new BufferedReader(
			new InputStreamReader(System.in)));

	int next() throws IOException {
		in.nextToken();
		return (int) in.nval;
	}

	void run() throws IOException {
		int cas = next();
		while (cas-- > 0) {
			int n = next();
			int m = next();
			int k = next();
			for (int i = 1; i <= n; i++) {
				p[i] = next();
				sum[i] = p[i] + sum[i - 1];
			}
			for (int i = 1; i + k - 1 <= n; i++)
				arr[i] = sum[i + k - 1] - sum[i - 1];
			st.init(1, n - k + 1, 1, arr);
			while (m-- > 0) {
				int op =next();
				int x = next();
				int y = next();
				if (op == 0) {
					int from = x - k + 1;
					if (from < 1)
						from = 1;
					int key = y - p[x];
					p[x] = y;
					st.update(from, x, 1, key);
				}
				if (op == 1) {
                    int from=x-k+1;
                    if(from<1)
                    	from=1;
                    int temp=p[x];
                    int key=p[y]-p[x];
                    p[x]=p[y];
                    st.update(from, x, 1, key);
                    from=y-k+1;
                    if(from<1)
                    	from=1;
                    key=temp-p[y];
                    p[y]=temp;
                    st.update(from, y, 1, key);
				}
				if(op==2)
				{
					int to=y-k+1;
					System.out.println(st.query(x, to, 1));
				}
			}
		}
	}
	public static void main(String[] args) throws IOException {
		new Garden().run();
	}
}

PS:再次感慨线段树的变化多端,虽然种过二三十颗树了还是驾驭不了,这几天继续种。。。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值