Just a Hook

题目

给定一个区间[1,N],里面的元素都是1,现在有M次区间修改的操作,每次会选择一个区间将其置为1或2,或3,问最后区间[1,N]的元素总和为多少?

分析

这是一道典型的区间覆盖问题,用线段树可以很好的解决。只需要在定义结点的时候,加上sum和tag就行。sum表示此结点对应区间的总和,tag表示要将其后代置为tag。tag = 1,2,3。所以pushdown和pushup就可以很容易想出来,具体看代码

AC代码

#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <string>
#include <cmath>
using namespace std;
typedef long long ll;
const ll maxn = 1e5+10;

ll T,N,M;
ll arr[maxn];
struct node{
    ll l,r,tag,sum;
}tr[4*maxn];
void pushup(ll u){
    node &fa = tr[u],&L = tr[u*2],&R = tr[u*2+1];
    fa.sum = L.sum + R.sum; //计算和简单加就可以了,因为加的时候其后代已经进行过pushdown操作了
}
void pushdown(ll u){
    node &fa = tr[u],&L = tr[u*2],&R = tr[u*2+1];
    if(fa.tag){ //如果要将后代置为tag
        L.sum = (L.r-L.l+1)*fa.tag; //重新计算和
        R.sum = (R.r-R.l+1)*fa.tag;
        L.tag = fa.tag; //后代传递
        R.tag = fa.tag;
        fa.tag = 0;
    }
}
void build(ll u,ll l,ll r){
    if(l == r){
        tr[u] = {l,r,1,1};
    }else{
        tr[u] = {l,r};
        ll mid = (l+r)>>1;
        build(u*2,l,mid);
        build(u*2+1,mid+1,r);
        pushup(u);
    }
}
void modify(ll u,ll l,ll r,ll tag){
    if(tr[u].l>=l && tr[u].r<=r){
        tr[u].sum = (tr[u].r-tr[u].l+1)*tag;
        tr[u].tag = tag;
    }else{
        pushdown(u);
        ll mid = tr[u].l+tr[u].r>>1;
        if(l<=mid) modify(u*2,l,r,tag);
        if(r>mid) modify(u*2+1,l,r,tag);
        pushup(u);
    }
}
ll query(ll u,ll l,ll r){
    if(tr[u].l>=l && tr[u].r<=r) return tr[u].sum;
    else{
        pushdown(u);
        ll mid = tr[u].l+tr[u].r>>1;
        ll sum = 0;
        if(l<=mid) sum += query(u*2,l,r);
        if(r>mid) sum += query(u*2+1,l,r);
        pushup(u);
        return sum;
    }
}
int main(){
    cin>>T;
    int kase = 0;
    while(T--){
        scanf("%lld %lld",&N,&M);
        build(1,1,N);
        ll a,b,c;
        while(M--){
            scanf("%lld %lld %lld",&a,&b,&c);
            modify(1,a,b,c);
        }
        printf("Case %d: The total value of the hook is %lld.\n",++kase,query(1,1,N));
    }
    return 0;
}