您的位置:首页 > 其它

HDU1698 线段树+区间更新+懒惰标记-Just a Hook

2016-08-15 14:44 573 查看
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

Sample Output

Case 1: The total value of the hook is 24.

题意:

Dota英雄屠夫的肉钩上面有n段,每段一开始是由铜构成,现在屠夫对其中x到y段更换材料,铜、银、金分别表示为1、2、3,如此进行多次操作,问最后所有段的总和。

题解:

本题如果用一个数组保存各段的值,每次更新操作之后就更新所有需要修改的值的话,肯定是不行的,需要的操作次数太多,所以我们可以用线段树的区间更新来完成这样需要频繁修改区间值的问题。

首先是建树:

void build(int r, int L, int R)
{
if(L == R)
{
sum[r] = 1;//每一段的初始值为1
return;
}
int M = (L+R)/2;
build(r*2,L,M);
build(r*2+1,M+1,R);
sum[r] = sum[r*2]+sum[r*2+1];
}


然后是对于每一次x、y的区间进行更新:

void push_down(int r)
{
int lc = r*2, rc = r*2+1;
//将懒惰标记向下传递
if(setv[r] >= 1)
{
setv[lc] = setv[rc] = setv[r];
setv[r] = 0;
}
}

void push_up(int r, int L, int R)
{
//向上更新
int lc = r*2, rc = r*2+1;
sum[r] = sum[lc] + sum[rc];
if(setv[r] >= 1)
sum[r] = setv[r] * (R-L+1);
}

void update(int r, int L, int R)
{
//如果[L,R]区间在[x,y]区间内,就可以在该区间设置懒惰标记并更新值,不必再向下更新
if(x <= L && R <= y)
{
setv[r] = v;
sum[r] = v * (R-L+1);
}
else
{
int M = (L+R)/2;
//因为要修改的部分有在r区间里面,所以懒惰标记要向下传递
push_down(r);
//如果左半边需要更新的话,就继续更新,如果不需要更新,因为懒惰标记向下传递了,所以需要更新左半部分的sum值
if(x <= M)
update(r*2,L,M);
else
push_up(r*2,L,M);
//右半部分同上
if(y > M)
update(r*2+1,M+1,R);
else
push_up(r*2+1,M+1,R);
//因为左右两部分的值进行了更新,所以需要进行向上更新
push_up(r,L,R);
}
}


最后是进行查询:

void query(int r, int L, int R)
{
//如果区间内有标记,直接求和即可,不需要再向下找
if(setv[r] >= 1)
ans += setv[r] * (min(R,qr)-max(L,ql)+1);
else if(ql <= L && R <= qr)
ans += sum[r];
else
{
int M = (L+R)/2;
if(ql <= M)
query(r*2,L,M);
if(qr > M)
query(r*2+1,M+1,R);
}
}


完整AC代码如下:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <cmath>
using namespace std;

const int MAXN = 100010;
//一般来说开4倍就可以了,但是开4倍RE,开到6倍才过
int sum[MAXN*6];
int setv[MAXN*6];
int ql,qr,x,y,v;
int ans;

void build(int r, int L, int R) { if(L == R) { sum[r] = 1;//每一段的初始值为1 return; } int M = (L+R)/2; build(r*2,L,M); build(r*2+1,M+1,R); sum[r] = sum[r*2]+sum[r*2+1]; }

void push_down(int r) { int lc = r*2, rc = r*2+1; //将懒惰标记向下传递 if(setv[r] >= 1) { setv[lc] = setv[rc] = setv[r]; setv[r] = 0; } } void push_up(int r, int L, int R) { //向上更新 int lc = r*2, rc = r*2+1; sum[r] = sum[lc] + sum[rc]; if(setv[r] >= 1) sum[r] = setv[r] * (R-L+1); } void update(int r, int L, int R) { //如果[L,R]区间在[x,y]区间内,就可以在该区间设置懒惰标记并更新值,不必再向下更新 if(x <= L && R <= y) { setv[r] = v; sum[r] = v * (R-L+1); } else { int M = (L+R)/2; //因为要修改的部分有在r区间里面,所以懒惰标记要向下传递 push_down(r); //如果左半边需要更新的话,就继续更新,如果不需要更新,因为懒惰标记向下传递了,所以需要更新左半部分的sum值 if(x <= M) update(r*2,L,M); else push_up(r*2,L,M); //右半部分同上 if(y > M) update(r*2+1,M+1,R); else push_up(r*2+1,M+1,R); //因为左右两部分的值进行了更新,所以需要进行向上更新 push_up(r,L,R); } }

void query(int r, int L, int R) { //如果区间内有标记,直接求和即可,不需要再向下找 if(setv[r] >= 1) ans += setv[r] * (min(R,qr)-max(L,ql)+1); else if(ql <= L && R <= qr) ans += sum[r]; else { int M = (L+R)/2; if(ql <= M) query(r*2,L,M); if(qr > M) query(r*2+1,M+1,R); } }

int main()
{
int T;
int n,m;
int t = 0;
scanf("%d",&T);
while(T--)
{
memset(setv,0,sizeof(0));
//从1到n一开始处于1标记下,注意,不然会出错
setv[1] = 1;
scanf("%d%d",&n,&m);
build(1,1,n);
for(int i = 1; i <= m; i++)
{
scanf("%d%d%d",&x,&y,&v);
update(1,1,n);
}
ans = 0;
ql = 1;
qr = n;
query(1,1,n);
printf("Case %d: The total value of the hook is %d.\n",++t,ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息