1. 程式人生 > >Just a Hook(線段樹)

Just a Hook(線段樹)

Just a Hook
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.
這是一個很直白的線段樹的題目,求區間的和

code:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int maxn = 100100;
int num,n,q;
int tre[maxn<<2],lazy[maxn<<2];//線段樹的陣列,懶惰標記陣列,開大於四倍(據說是因為經驗所以開四倍)
//建立線段樹
void Build(int l,int r,int rt){
    if(l==r){//如果l==r到了葉子,直接賦值
        tre[rt] = 1;//題目要求初始為1
        return;
    }
    int mid = l+r>>1;//取中點值
    Build(lson);
    Build(rson);//遞迴左右建樹
    tre[rt] = tre[rt<<1]+tre[rt<<1|1];//回溯過程中給父節點返回子節點的資訊
}
//懶惰標記的向下調整
void pushDown(int l,int r,int rt){
    lazy[rt<<1] = lazy[rt<<1|1] = lazy[rt];//把懶惰標記傳給原來節點的兩個兒子
    int mid = l+r>>1;//劃分出左右兒子
    tre[rt<<1] = (mid-l+1)*lazy[rt];
    tre[rt<<1|1] = (r-mid)*lazy[rt];//更新兒子的值
    lazy[rt] = 0;//更新結束原來那個點的懶惰標記可以消失了
}
//對線段樹進行更新,這裡是求一段的和
void Update(int x,int y,int z,int l,int r,int rt){
    if(x<=l&&y>=r){
        tre[rt] = (r-l+1)*z;//更新這一區間的資訊
        lazy[rt] = z;//並把這個點進行懶惰標記,不繼續向下更新
        return ;
    }
    if(lazy[rt])pushDown(l,r,rt);//如果需要繼續向下尋找,則如果懶惰標記存在,就要向下更新
    int mid = l+r>>1;
    if(x<=mid)Update(x,y,z,lson);//如果左子樹中有要查詢的,就要遍歷左樹
    if(y>mid)Update(x,y,z,rson);//同理如果右子樹中含有要查詢的,就要遍歷右樹
    tre[rt] = tre[rt<<1]+tre[rt<<1|1];//更新完後回溯返回給父節點資訊
}
int main(){
    int i;
    int cases = 0;
    scanf("%d",&num);
    while(num--){
        memset(tre,0,sizeof(tre));
        memset(lazy,0,sizeof(lazy));//先初始化
        scanf("%d",&n);
        Build(1,n,1);//建樹
        scanf("%d",&q);
        int x,y,z;
        while(q--){
            scanf("%d%d%d",&x,&y,&z);
            Update(x,y,z,1,n,1);
        }
        printf("Case %d: The total value of the hook is %d.\n",++cases,tre[1]);
    }
    return 0;
}