线段树-区间更新

今天初学线段树,刚开始听的时候感觉还好,但是到自己写代码的时候总会有一些问题,在此记录一下自己的学习过程。
对于线段树的区间更新,要比单个节点的更新要复杂些。这里我一HDU上的1698为例,题目如下

http://acm.hdu.edu.cn/showproblem.php?pid=1698
Just a Hook

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

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.
题目的意思是,给出一个长度为N的一组数,初始时都为1,根据每次输入的命令,来改变指定区间叶子节点的值,并向上更新, 最后再计算这N个数的和。


在这里先讲一下延时更新的问题,刚开始自己也不明白,还是亲自动手画一画会比较清楚。延时更新的目的就是,在我需要用到某一区间节点时,才更新此节点,附录代码中的数组col就是起这个作用。它用来表示,这一区间每一个下标对应的数应该改变为多少。

#include
#include
#include
using namespace std;

#define ML 111111
long long sum[ML<<2];   //表示各节点所表示的区间和
int col[ML<<2];     //延迟更新的标记数组

void PushUp(int rt)
{
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];    //向上更新节点
}

void PushDown(int n, int m)
{
    if(col[n])  //延迟更新,当col[n]不为0时才向下更新col[n]的左右子数
    {
        col[n<<1] = col[n<<1|1] = col[n];   //更新左右子树
        sum[n<<1] = (m- (m>>1))*col[n];
        sum[n<<1|1] = (m>>1) * col[n];
        col[n] = 0;
    }
}

void build(int l, int r, int rt)    //构建线段树
{
    sum[rt] = 1;
    col[rt] = 0;
    if(l==r)
        return;
    int mid = (l+r)/2;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    PushUp(rt);
}

void Upgrade(int L, int R, int c, int l, int r, int rt)
{
    if(l>=L&&r<=R)  //如果l,r是L,R的子集就更新sum[rt]和col[rt]的值
    {
        col[rt] = c;
        sum[rt] = (r-l+1) * c;
        return;
    }
    PushDown(rt, r-l+1);
    int mid = (l+r)/2;
    if(L<=mid)
    {
        Upgrade(L,R,c,l,mid,rt<<1);
    }
    if(R>mid)
    {
        Upgrade(L,R,c,mid+1,r,rt<<1|1);
    }
    PushUp(rt);
}

int main(int argc, char *argv[])
{
    int t;
    cin>>t;
    int cnt = 1;
    while(t--)
    {
        int n, m;
        scanf("%d %d", &n, &m);
        build(1,n,1);
        for (int i=0; iint numa, numb, numc;
            scanf("%d %d %d", &numa, &numb, &numc);
            Upgrade(numa,numb,numc,1,n,1);
        }
        printf("Case %d: The total value of the hook is %d.\n", cnt, sum[1]);
        cnt++;
    }
    return 0;
}

你可能感兴趣的:(线段树-区间更新)