HDU-1698 Just a Hook

Just a Hook

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 45734    Accepted Submission(s): 21790


 

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

 

 

Sample Output

 

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

 题意比较简单,在给定的数字序列中,有两种操作:1、对于给定的区间将所在区间的数改为指定值 2、对于给定的区间计算对应区间中的和,刚开始只会简单的暴力改变数值,暴力计算和,但是会超时,想了很久不会,最后看到要用线段树进行存储计算,正好学习一波...

 线段树是一棵二叉搜索树,每个点储存的是一个区间的信息,这个区间的信息由该点进行管理,每个节点包含其所管理区间的左右端点[l,r],以及所需维护的信息,在这道题目里面是该区间内的数字的和以及更改时的标记。具体线段树的操作可以查看博客http://www.cnblogs.com/TheRoadToTheGold/p/6254255.html进行学习

 

线段树是一棵完全二叉树,基本思想为:二分

在这其中有一个lazy标志用来更新等操作,主要思想为当用到它的时候才去检查lazy标志,看是否需要更新,需要才进行,不需要则不用。比如有一个操作要求修改[4,8]区间之间的值为a,当updata函数从根节点进行递归操作时,会将更新标志逐级向下传递,在传递到自己的孩子节点后,让其孩子节点进行更改,最后递归结束后,再根据孩子节点的更改情况更新自身。当到达的某个节点管理的区间后,其只是改变了该节点中所代表的区间的总和,但并没有改变[4,8]的孩子节点内的内容(即其孩子节点所管理的区间内的点的数值(不是管理区间的和,在该题中即为题中的1,2,3)没有修改为a),那么当需要查询[5,7]中的数字和时如果没有进行修改便会出错。这里的lazy标志便可以起到作用,管理[4,8]区间的点的lazy标志代表需要进行修改时,便可以在查询时继续调用递归函数进行修改,这里就做到了当需要用的时候才进行更新

 

#include <cstdio>
#include <iostream>
using namespace std;
int T,N,Q,X,Y,Z;
typedef struct                               //l,r表示该点代表的区间(l<=r),value在这里表示该区间内的数字和
{
    int l,r,value,flag;                      //flag为改变标记,在这里用来表示该节点所管理的区间的每个点的数值
}Node;
const int maxnum=100005;
Node a[maxnum<<2];

inline void build(int root,int ll,int rr)    //构造线段树
{
    a[root].l=ll;
    a[root].r=rr;
    a[root].flag=0;
    if(ll==rr)                               //到达叶节点,初始化值为1
    {
        a[root].value=1;
        return;
    }
    int mid=(ll+rr)/2;
    build(root*2,ll,mid);                    //递归构建左子树
    build(root*2+1,mid+1,rr);                //递归构建右子树
    a[root].value=a[root*2].value+a[root*2+1].value;  //该节点的值为两个孩子节点的值的和(孩子节点平分了该区间)
}

inline void pushdown(int root)               //将节点的改变情况下传到孩子节点(类似于懒加载)
{
    a[root<<1].flag=a[(root<<1)+1].flag=a[root].flag;  //更新标记
    a[root<<1].value=a[root].flag*(a[root<<1].r-a[root<<1].l+1);    //更新左孩子值
    a[(root<<1)+1].value=a[root].flag*(a[(root<<1)+1].r-a[(root<<1)+1].l+1);   //更新右孩子的值
    a[root].flag=0;                                    //更新完毕,清除该节点标记
}

inline void updata(int ll,int rr,int root,int ans)       //更新
{
    if(a[root].l==ll&&a[root].r==rr)                     //如果找到对应的更新区间,进行更新
    {
        a[root].flag=ans;                                //管理区间的点的数值更新
        a[root].value=(a[root].r-a[root].l+1)*ans;       //更新该点的和
        return;
    }
    if(a[root].flag) pushdown(root);                     //将当前节点的改变状态传递到孩子
    int mid=(a[root].l+a[root].r)>>1;
    if(mid>=rr) updata(ll,rr,(root<<1),ans);             //rr<=mid说明该区间处于该节点的左孩子所管理的区间内
    else if(mid<ll) updata(ll,rr,((root<<1)+1),ans);     //mid<ll说明该区间处于该节点的右孩子所管理的区间内
    else                                                 //以上都不是说明[ll,rr]横跨该节点的左右孩子所管理的区间
    {
        updata(ll,mid,(root<<1),ans);                    //更新属于左孩子管理的区间
        updata(mid+1,rr,((root<<1)+1),ans);              //更新属于右孩子管理的区间
    }
    a[root].value=a[root*2].value+a[root*2+1].value;     //递归更新完其左右孩子后,更新自身所管理区间的点的和
}

inline int query(int root,int ll,int rr)                 //查询某段区间内的和
{
    if(a[root].l==a[root].r) return a[root].value;        //该点管理的区间即为所查询的区间,直接返回其要求的和
    if(a[root].flag) pushdown(root);                      //若当前节点需要改变,说明其孩子节点也需改变
    int mid=(a[root].r+a[root].l)>>1;
    if(mid>=rr) return query((root<<1),ll,rr);            //rr<=mid说明查询区间处于其左孩子管理区间内
    else if(mid<ll) return query(((root<<1)+1),ll,rr);    //ll>mid说明查询区间处于其右孩子管理区间内
    else return query((root<<1),ll,mid)+query(((root<<1)+1),mid+1,rr);  //否则说明处于横跨其左右孩子,分别递归询问并将其相加
}

int main()
{
    scanf("%d",&T);
    for(int t=1;t<=T;t++)
    {
        scanf("%d",&N);
        scanf("%d",&Q);
        build(1,1,N);
        while(Q--)
        {
            scanf("%d%d%d",&X,&Y,&Z);
            updata(X,Y,1,Z);
        }
        printf("Case %d: The total value of the hook is %d.\n",t,query(1,1,N));
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值