HDU 3397 Sequence operation

这道题又做得颇有感触...线段树好像总是很灵活,不过我觉得它应该也是有章可循的
这次用自己配的gvim写,感觉还不错
这次还学到一点技巧,就是无限WA时可以写个暴力程序对拍,对于这道题虽然没法一步一步跟踪数据,但是这样的对拍还是给了我许多启发

线段树应该记录cover表示当前节点是否覆盖,以及覆盖的类型
然后还是三部曲:进入子节点之前分配信息,递归调用子节点,从子节点回来合并信息


/*
 * Author: rush
 * Created Time:  2010年05月01日 星期六 15时17分10秒
 * File Name: icpc/hdu/3397.cpp
 */
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using std::swap;
using std::max;

int max3(int a, int b, int c)
{ return max(a, max(b, c)); }
int max4(int a, int b, int c, int d)
{ return max(a, max3(b, c, d)); }

const int size = 100000 + 5;
struct node_t {
	int L0, M0, R0;
	int L1, M1, R1;
	int ONE;
	int range;
	int cover; // 覆盖情况(-1是未覆盖,0, 1, 2如题) 
	void reset(const int _r)
	{
		range = _r;
		L0 = M0 = R0 = range;
		L1 = M1 = R1 = 0;
		ONE = 0;
	}
	void perform(int op)
	{
		switch (op) {
		case 0: 
			L0 = M0 = R0 = range;
			L1 = M1 = R1 = 0;
			ONE = 0;
			cover = 0;
			break;
		case 1:
			L0 = M0 = R0 = 0;
			L1 = M1 = R1 = range;
			ONE = range;
			cover = 1;
			break;
		case 2:
			swap(L0, L1), swap(M0, M1), swap(R0, R1);
			ONE = range - ONE;
			// 这里很关键,根据之前的覆盖情况确定现在的覆盖情况,
			// 其实对于case 0和case 1也有这个问题,不过因为是直接改变最终值为0或1,
			// 而不是像case 2这样根据之前情况改变,所以那里没有这样分情况处理 
			switch (cover) { 
			case -1:
				cover = 2; break;
			case 0:
				cover = 1; break;
			case 1:
				cover = 0; break;
			case 2:
				cover = -1; break;
			}
			break;
		}
	}
} tree[size * 3];
int n, m;

void plant(int low, int high, int node)
{
	tree[node].reset(high - low + 1);
	tree[node].cover = -1;
	int mid = (low + high) / 2;
	// low < high 恰好能够处理[x, x]的区间(即对于单个点构成的区间) 
	if (low < high)
	{
		plant(low, mid, node * 2);
		plant(mid + 1, high, node * 2 + 1);
	}
}

int left, right;
void update(int low, int high, int node, int op)
{
	if (left <= low && high <= right) {
		tree[node].perform(op);
	} else if (left <= high && low <= right) {
		int mid = (low + high) / 2;
		// 进入递归前,如果当前结点被覆盖,则将其属性传递给子节点 
		if (tree[node].cover != -1)
		{
			tree[node * 2].perform(tree[node].cover);
			tree[node * 2 + 1].perform(tree[node].cover);
			tree[node].cover = -1;
		}
		// 递归调用左右子节点,这里不用判断low < high是因为如果有单点区间,
		// 则单点一定会被本函数的第一个if考虑到 
		update(low, mid, node * 2, op);
		update(mid + 1, high, node * 2 + 1, op);
		// 从左右子节点回来,更新父节点的信息
		node_t &pnt = tree[node], &lch = tree[node * 2], &rch = tree[node * 2 + 1];
		pnt.L0 = lch.L0 + (lch.L0 == lch.range ? rch.L0 : 0);
		pnt.M0 = max3(lch.M0, lch.R0 + rch.L0, rch.M0);
		pnt.R0 = rch.R0 + (rch.R0 == rch.range ? lch.R0 : 0);

		pnt.L1 = lch.L1 + (lch.L1 == lch.range ? rch.L1 : 0);
		pnt.M1 = max3(lch.M1, lch.R1 + rch.L1, rch.M1);
		pnt.R1 = rch.R1 + (rch.R1 == rch.range ? lch.R1 : 0);

		pnt.ONE = lch.ONE + rch.ONE;
	}
}
// 其实只需要记录询问的区间被拆成哪2 * log n个子区间就行了,
// 所以不按常规的做法,而是像我这样把它们标记出来也是一样的 
int a[32 * 2], la;
void query(int low, int high, int node)
{
	if (left <= low && high <= right) {
		a[la++] = node;
	} else if (left <= high && low <= right) {
		int mid = (low + high) / 2;
		// 查询时也需要将父节点信息传递给子节点,
		// 这是因为前面update函数只能保证从2*logn个子区间往上的信息是对的 
		// 往下的没有做更新(如果做了就是指数的复杂度了) 
		if (tree[node].cover != -1)
		{
			tree[node * 2].perform(tree[node].cover);
			tree[node * 2 + 1].perform(tree[node].cover);
			tree[node].cover = -1;
		}
		query(low, mid, node * 2);
		query(mid + 1, high, node * 2 + 1);
	}
}

int main()
{
	freopen("data.in", "r", stdin);
	freopen("my.out", "w", stdout);
	int T;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d%d", &n, &m);
		plant(0, n - 1, 1);
		int t;
		for (int i = 0; i < n; ++i)
		{
			scanf("%d", &t);
			left = right = i;
			update(0, n - 1, 1, t);
		}
		for (int i = 0; i < m; ++i)
		{
			scanf("%d%d%d", &t, &left, &right);
			if (t < 3) {
				update(0, n - 1, 1, t);
			} else {
				la = 0;
				query(0, n - 1, 1);
				if (t == 3) {
					int ans3 = 0;
					for (int j = 0; j < la; ++j)
						ans3 += tree[a[j]].ONE;
					printf("%d\n", ans3);
				} else {
					int ans4 = 0;
					for (int j = 0; j < la; ++j)
						ans4 = max(ans4, tree[a[j]].M1);
					int tmp = 0;
					for (int j = 0; j < la; ++j)
						if (tree[a[j]].ONE == tree[a[j]].range) {
							tmp += tree[a[j]].range;
						} else {
							tmp += tree[a[j]].L1;
							ans4 = max(ans4, tmp);
							tmp = tree[a[j]].R1;
						}
					ans4 = max(ans4, tmp);
					printf("%d\n", ans4);
				}
			}
		}
	}
    return 0;
}



你可能感兴趣的:(C++,c,C#,J#)