Just a Hook (hdu 1698 线段树区间更新)

Just a Hook

Time Limit : 4000/2000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other)

Problem Description
In the game of DotA, Pudge’s meat hook is actually the most horrible thing for most of the heroes. The hook is made up of several consecutive metallic sticks which are of the same length.



Now Pudge wants to do some operations on the hook.

Let us number the consecutive metallic sticks of the hook from 1 to N. For each operation, Pudge can change the consecutive metallic sticks, numbered from X to Y, into cupreous sticks, silver sticks or golden sticks.
The total value of the hook is calculated as the sum of values of N metallic sticks. More precisely, the value for each kind of stick is calculated as follows:

For each cupreous stick, the value is 1.
For each silver stick, the value is 2.
For each golden stick, the value is 3.

Pudge wants to know the total value of the hook after performing the operations.
You may consider the original hook is made up of cupreous sticks.
 

Input
The input consists of several test cases. The first line of the input is the number of the cases. There are no more than 10 cases. For each case, the first line contains an integer N, 1<=N<=100,000, which is the number of the sticks of Pudge’s meat hook and the second line contains an integer Q, 0<=Q<=100,000, which is the number of the operations. Next Q lines, each line contains three integers X, Y, 1<=X<=Y<=N, Z, 1<=Z<=3, which defines an operation: change the sticks numbered from X to Y into the metal kind Z, where Z=1 represents the cupreous kind, Z=2 represents the silver kind and Z=3 represents the golden kind.
 

Output
For each case, print a number in a line representing the total value of the hook after the operations. Use the format in the example.
 

Sample Input
  
  
1 10 2 1 5 2 5 9 3
 
//题意:屠夫的钩子被拿来当题了!!!屠夫的钩子是由n段长度相同的棒子构成的(涨姿势了),棒子有3种类型,铜的(价值为1),银的(价值为2),金的(价值为3),一开始的时候n段棒子都是铜的,即每段棒子价值都为1。然后输入Q,接下来Q行每行3个数,x,y,z,表示x-y这几段棒子都变成铜/银/金。最后让你求n段棒子的总价值。

//思路:看到是区间更新,首先就应该想到线段树。用了线段树,如果每次更新都更新到叶子节点的话,肯定是会TLE的,所以这里用 延迟标记很关键。延迟标记其实没那么玄乎,就是结构体里多定义了一个int变量。如果要改变一个区间里的值,发现线段树里有一个节点表示的就是这个区间,那么还要继续递归下去吗?不用,直接改变这个节点的值就行了。但是如果下次要访问它的子节点,它子节点的值是还没改变之前的,怎么办?我们的做法是,在这次改变了这个节点的值后,给这个节点标记一下, 如果下次再访问到这里,发现它这里有个标记,那么就把它的值传给他左右两个孩子(相当于延迟了把值传给它孩子的时间,延迟标记很形象有木有),再把它的这个标记去掉(防止重复),再继续递归。

该题的思路详见代码,都注释了


#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;

const int MAX = 100000 + 100;

typedef struct {
	int left, right;
	int val;
}segTree;

segTree tree[MAX * 4];

//l初值为左边界(0),r初值右边界(n-1)
void build(int root, int l, int r)
{
	tree[root].left = l;
	tree[root].right = r;

	//根据题意,val初始值为1
	tree[root].val = 1;

	//到达叶子结点就return
	if (l == r)
		return;

	//不是叶子结点的话就二分
	int mid = (l + r) / 2;
	build(root * 2 + 1, l, mid);
	build(root * 2 + 2, mid + 1, r);

	return;
}

//l,r,v初值分别为输入的x,y,z
void update(int root, int l, int r, int v)
{
	//这个节点的值若>0,说明它下面的子节点都是同色的
	//若这个节点的值还等于v,说明它所代表的这一段已经染成我们想要的颜色了,直接返回
	if (tree[root].val == v)
		return;

	//若这个节点代表的区间刚刚好是满足要求的
	//那就把它染成v,说明他下面的子节点都是v了
	if (tree[root].left == l&&tree[root].right == r)
	{
		tree[root].val = v;
		return;
	}

	//若这个节点的值>0,让它的左右孩子都等于他的值,然后把它的值设为0
	//这样表明它的下面的节点都是同色的
	if (tree[root].val)
	{
		tree[root * 2 + 1].val = tree[root].val;
		tree[root * 2 + 2].val = tree[root].val;
		tree[root].val = 0;
	}

	int mid = (tree[root].left + tree[root].right) / 2;

	//若l>=这个节点的右孩子的left,那么这个节点的左孩子就不用考虑了
	if (l > mid)
		update(root * 2 + 2, l, r, v);

	//同理,若r<这个节点的右孩子的left,那么这个节点的右孩子就不用考虑了
	else if (r <= mid)
		update(root * 2 + 1, l, r, v);

	//最后必然会在前2个if处return,最多递归到叶子节点
	else
	{
		update(root * 2 + 1, l, mid, v);
		update(root * 2 + 2, mid + 1, r, v);
	}

	return;
}

int getnum(int root, int l, int r)
{
	//如果这个节点的val不等于0,说明它代表的区间是纯色的
	//那么只要它的区间长度*它的val就行了
	if (tree[root].val)
	{
		return (tree[root].right - tree[root].left + 1)*tree[root].val;
	}

	//若这个节点的val等于0,那么说明它代表的区间是不纯色的
	//那么就是它左孩子的val+右孩子的val(递归)
	int mid = (tree[root].left + tree[root].right) / 2;
	return getnum(root * 2 + 1, l, mid) + getnum(root * 2 + 2, mid + 1, r);
}

int main()
{
	int n;
	int Q;
	int T, Case = 1;
	scanf("%d", &T);
	while (T--)
	{
		int x, y, z;
		scanf("%d%d", &n, &Q);
		build(0, 0, n - 1);
		for (int i = 0; i < Q; i++)
		{
			scanf("%d%d%d", &x, &y, &z);
			//因为上面的函数的root都是从0开始的
			//而输入的x,y是从1开始的
			//所以x--,y--
			x--;
			y--;
			update(0, x, y, z);
		}
		int ans = getnum(0, 0, n - 1);
		printf("Case %d: The total value of the hook is %d.\n", Case++, ans);
	}
	return 0;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值