1. 程式人生 > >Just a Hook (hdu 1698 線段樹區間更新)

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;
}