您的位置:首页 > 其它

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

2016-08-16 23:57 330 查看

题目链接: 传送门

Just a Hook

Time Limit: 1000MS     Memory Limit: 32768 K

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 Iutput

1
10
2
1 5 2
5 9 3

Sample Output

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

解题思路


肯定不能用单点更新的办法来逐个更新一个区间,而正解的区间更新一开始也看得我云里雾里,最好的办法就是跟着程序走一遍,调试一遍。

懒惰标记是整个代码的核心,搞懂了懒惰标记也就理解了整个算法。懒惰标记实际上就是让叶子节点暂时处于不更新状态,用到的时候再更新,代码中我用col[]数组用来作为懒惰标记,假设区间总长度是1-10,我们现在想要更新1-6,(将1-6的值都加3)那么update()会先找1-10,发现不合适,再找他的左右孩子,发现1<5,说明1-6的区间在1-10的左孩子中,同时6>5,1-6也在1-10的右孩子中,这样依次去找1-6在的区间。但是找到1-5的时候,我们发现整个1-5都在1-6中间,也就是说这一段都要更新,那么我们将1-5的sum值更新了,同时用col[rt]+=3记录下来1-5中的数字现在每个都要加的数字,但是1-5下边还有1-3,4-5,3-3,4-4,5-5,这些我们就可以不用更新,因为这些我们暂时还用不到,假如现在又要将1-5区间的值都加5,那么col[rt]+=5,此时就是8了,但是还是不用更新他的子节点,假如我们现在要用到1-3区间了,我们就可以一次性给1-3区间加上8,而不用先加3,再加5,这样懒惰标记就使得每次的递归都少了好多。



以上图为例,我先更新[5-7]区间的数为3,此时程序跑完之后会将sum[3] = 3(sum数组下标即为叶子结点),而节点3之后的叶子节点暂不更新,之后我又更新了[4-7]区间的数为2,程序跑一遍,将sum[3] = 2(叶子节点不再往下更新),sum[11] = 2,最后我更新了[5-6]区间的数为1,因为区间[5-6]包含在区间[5-7]里面,所以程序往[5-7]这个大节点跑下去,节点3之前打过懒惰标记,此时程序将更新之前在[5-7]区间未更新的叶子节点,按照完全二叉树建立的特点很容易明白
sum[rt<<1] = (m - (m >> 1)) * col[rt];sum[rt<<1|1] = (m >> 1) * col[rt];
这两句代码中col[rt]前面的系数为什么如此写。如果程序最后更新的是[4-6]区间,那么程序出了跑[5-7]这个大区间之外,额外跑一个节点11.


#include<cstdio>
#include<algorithm>
using namespace std;
#define lson l ,m ,rt << 1
#define rson m + 1 ,r , rt << 1 | 1
const int maxn = 100005;
int sum[maxn<<2],col[maxn<<2];

void PushUp(int rt)
{
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}

void PushDown(int rt,int m)
{
if (col[rt])
{
col[rt<<1] = col[rt<<1|1] = col[rt];
sum[rt<<1] = (m - (m >> 1)) * col[rt];
sum[rt<<1|1] = (m >> 1) * col[rt];
col[rt] = 0;
}
}

void build(int l,int r,int rt)
{
col[rt] = 0;
sum[rt] = 1;
if (l == r) return;
int m = (l + r) >> 1;
build(lson);
build(rson);
}

void upd(int L,int R,int c,int l,int r,int rt)
{
if (L <= l && r <= R)
{
col[rt] = c;
sum[rt] = (r - l + 1) * c;
return;
}
PushDown(rt,r - l + 1);
int m = (l + r) >> 1;
if (L <= m) upd(L,R,c,lson);
if (R > m)  upd(L,R,c,rson);
PushUp(rt);
}

int main()
{
int T;
scanf("%d",&T);
for (int i =1;i <= T;i++)
{
int n,m,x,y,z;
scanf("%d%d",&n,&m);
build(1,n,1);
while (m--)
{
scanf("%d%d%d",&x,&y,&z);
upd(x,y,z,1,n,1);
}
printf("Case %d: The total value of the hook is %d.\n",i,sum[1]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: