4927 线段树练习5 (多重标记下放)

题目描述 Description

有n个数和5种操作

add a b c:把区间[a,b]内的所有数都增加c

set a b c:把区间[a,b]内的所有数都设为c

sum a b:查询区间[a,b]的区间和

max a b:查询区间[a,b]的最大值

min a b:查询区间[a,b]的最小值

输入描述 Input Description

第一行两个整数n,m,第二行n个整数表示这n个数的初始值

接下来m行操作,同题目描述

输出描述 Output Description

对于所有的sum、max、min询问,一行输出一个答案

样例输入 Sample Input

10 6

3 9 2 8 1 7 5 0 4 6

add 4 9 4

set 2 6 2

add 3 8 2

sum 2 10

max 1 7

min 3 6

样例输出 Sample Output

49

11

4

数据范围及提示 Data Size & Hint

10%:1<n,m<=10

30%:1<n,m<=10000

100%:1<n,m<=100000

保证中间结果在long long(C/C++)、int64(pascal)范围内

思路;

有两个标记一个add一个set,我们肯定是优先处理set操作,如果当前点被set标记了,那么之前的add标记也会被清空,所以当进行set操作是必须要清空add标记。

题目没给 c 的范围,那么我们假定他为 -1e9 < c < 1e9 ,那么也就是有可能存在等于0和负数的情况,所以set标记的初始化可以选定为 -1e16;

为社么感觉写过。。。

实现代码;

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mid ll m = (l + r) >> 1
const ll M = 1e5 + 10;
const ll inf = 1e16+10;
ll sum[M<<2],Set[M<<2],mx[M<<2],add[M<<2],mn[M<<2];
ll a[M];
void pushup(ll rt){
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
    mx[rt] = max(mx[rt<<1],mx[rt<<1|1]);
    mn[rt] = min(mn[rt<<1],mn[rt<<1|1]);
}

void pushdown(ll l,ll r,ll rt){
    if(Set[rt]!=-inf){
        mid;
        sum[rt<<1] = Set[rt]*(m-l+1);
        sum[rt<<1|1] = Set[rt]*(r-m);
        mx[rt<<1] = mx[rt<<1|1] = Set[rt];
        mn[rt<<1] = mn[rt<<1|1] = Set[rt];
        Set[rt<<1] = Set[rt<<1|1] = Set[rt];
        add[rt<<1] = add[rt<<1|1] = 0;
        Set[rt] = -inf;
    }
    if(add[rt]){
        mid;
        sum[rt<<1] += add[rt]*(m-l+1);
        sum[rt<<1|1] += add[rt]*(r-m);
        mx[rt<<1] += add[rt]; mx[rt<<1|1] += add[rt];
        mn[rt<<1] += add[rt]; mn[rt<<1|1] += add[rt];
        add[rt<<1] += add[rt]; add[rt<<1|1] += add[rt];
        add[rt] = 0;
    }
}

void build(ll l,ll r,ll rt){
    Set[rt] = -inf; add[rt] = 0;
    if(l == r){
        sum[rt] = mx[rt] = mn[rt] = a[l];
        Set[rt] = -inf;add[rt] = 0;
        return ;
    }
    mid;
    build(lson); build(rson);
    pushup(rt);
}

void update(ll L,ll R,ll op,ll c,ll l,ll r,ll rt){
    if(L <= l&&R >= r){
         if(op == 1){
             sum[rt] = (r-l+1)*c;
             mx[rt] = mn[rt] = Set[rt] = c;
             add[rt] = 0;
             return ;
         }
         else{
            sum[rt] += (r-l+1)*c;
            mx[rt] += c; add[rt] += c;
            mn[rt] += c;
            return ;
         }
    }
    pushdown(l,r,rt);
    mid;
    if(L <= m) update(L,R,op,c,lson);
    if(R > m) update(L,R,op,c,rson);
    pushup(rt);
}

ll query_sum(ll L,ll R,ll l,ll r,ll rt){
    if(L <= l&&R >= r){
        return sum[rt];
    }
    pushdown(l,r,rt);
    mid; ll ret = 0;
    if(L <= m) ret += query_sum(L,R,lson);
    if(R > m) ret += query_sum(L,R,rson);
    return ret;
}

ll query_max(ll L,ll R,ll l,ll r,ll rt){
    if(L <= l&&R >= r){
        return mx[rt];
    }
    pushdown(l,r,rt);
    mid; ll ret = -inf;
    if(L <= m) ret = max(ret,query_max(L,R,lson));
    if(R > m) ret = max(ret,query_max(L,R,rson));
    return ret;
}

ll query_min(ll L,ll R,ll l,ll r,ll rt){
    if(L <= l&&R >= r){
        return mn[rt];
    }
    pushdown(l,r,rt);
    mid,ret = inf;
    if(L <= m) ret = min(ret,query_min(L,R,lson));
    if(R > m) ret = min(ret,query_min(L,R,rson));
    return ret;
}

char s[100];
int main(){
    ll n,l,r,c,q;
    scanf("%lld%lld",&n,&q);
    for(ll i = 1;i <= n;i ++)
        scanf("%lld",&a[i]);
    build(1,n,1);
    while(q--){
        scanf("%s",s); scanf("%lld%lld",&l,&r);
        if(s[0] == ‘a‘){
            scanf("%lld",&c);
            update(l,r,0,c,1,n,1);
        }
        else if(s[0] == ‘s‘&&s[1] == ‘e‘){
            scanf("%lld",&c);
            update(l,r,1,c,1,n,1);
        }
        else if(s[0] == ‘s‘&&s[1] == ‘u‘){
             printf("%lld\n",query_sum(l,r,1,n,1));
        }
        else if(s[0] == ‘m‘&&s[1] == ‘a‘){
            printf("%lld\n",query_max(l,r,1,n,1));
        }
        else {
            printf("%lld\n",query_min(l,r,1,n,1));
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/kls123/p/9898654.html

时间: 2024-08-28 17:54:28

4927 线段树练习5 (多重标记下放)的相关文章

[hiho 22]线段树-lazy标记的下放

题目描述 之前提到过,线段树之所以更新查询快,是因为区间更新有lazy标记使得不需要每次都操作到叶子节点. 但是如果要操作一个节点时,其父节点上的lazy标记应当被释放,否则该节点无法得到最新的正确结果. 因而lazy标记下放的策略是在需要操作某个节点的子节点时,将该节点的lazy标记全部下放.见第69行. 同时应当注意,给某个节点增加lazy标记时,不要忘了修改该节点的相关统计值.因为更新完该节点后还要马上修改其父节点的统计值.见第80行. 代码如下: #include <stdio.h>

hdu 3397 Sequence operation (线段树 区间合并 多重标记)

链接:http://acm.hdu.edu.cn/showproblem.php?pid=3397 题意: 给你一串01串,有5种操作 0. 区间全部变为0 1.区间全部变为1 2.区间异或 3.询问区间1的个数 4.询问区间被最长连续1的长度 思路: 这5个操作都是比较基础的线段树操作,难点在于有两种修改操作,这类题之前也写过,之前是乘法和加法,这个是区间亦或和区间更新值,但是思路是可以借鉴的,我们要推出这两个操作的关系,这样才能维护好这两个标记,我们用两个标记:same , rev ,分别表

codevs 4927 线段树练习5

赶在期末考试之前把这道傻逼题调了出来. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 100500 using namespace std; long long n,m,a[maxn]; long long root,tot=0,ls[maxn<<2],rs[maxn<<2]; long long lazy[

codves 4927 线段树练习5

有n个数和5种操作 add a b c:把区间[a,b]内的所有数都增加c set a b c:把区间[a,b]内的所有数都设为c sum a b:查询区间[a,b]的区间和 max a b:查询区间[a,b]的最大值 min a b:查询区间[a,b]的最小值 输入描述 Input Description 第一行两个整数n,m,第二行n个整数表示这n个数的初始值 接下来m行操作,同题目描述 输出描述 Output Description 对于所有的sum.max.min询问,一行输出一个答案

线段树练习5(codevs 4927)

题目描述 Description 有n个数和5种操作 add a b c:把区间[a,b]内的所有数都增加c set a b c:把区间[a,b]内的所有数都设为c sum a b:查询区间[a,b]的区间和 max a b:查询区间[a,b]的最大值 min a b:查询区间[a,b]的最小值 输入描述 Input Description 第一行两个整数n,m,第二行n个整数表示这n个数的初始值 接下来m行操作,同题目描述 输出描述 Output Description 对于所有的sum.ma

bzoj1798: [Ahoi2009]Seq 维护序列seq(线段树多重标记下传)

www.cnblogs.com/shaokele/ bzoj1798: [Ahoi2009]Seq 维护序列seq Time Limit: 30 Sec Memory Limit: 64 MB Description 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,-,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值

Bzoj 3050: [Usaco2013 Jan]Seating(线段树裸题,然而区间修改标记下放和讨论Push_up很揪心)

题目链接 题意:开始有一个空白的区间,每次可能进行两个操作:A 将一个长度为p的区间加入一段连续空白的位置 L:一个区间恢复空白:要求出A不能进行的次数. 非常裸的线段树题目,用线段树统计最大的空白区间,每个节点需要记录当前区间的最长空白区间,从左端开始的最长空白区间,从右端开始的最长空白区间.Push_up的时候要讨论下,可以分别取[l,mid]和[mid+1,r]的最大空白区间,也可以用[l,mid]的从右端开始的最长空白区间+[mid+1,r]从左端开始的最大空白区间. 每次A的时候,就查

ACM-ICPC 2018 焦作赛区网络预赛 E Jiu Yuan Wants to Eat (树链剖分+线段树)

题目链接:https://nanti.jisuanke.com/t/31714 题意:给你一棵树,初始全为0,有四种操作: 1.u-v乘x    2.u-v加x   3. u-v取反  4.询问u-v的和 思路: 除去第三个操作就是很简单的树链剖分+线段树多重标记下放,所以我们只要考虑怎么维护第三个操作就好了, 由题目给的取反可知:!x =  (2^64-1) - x;   但是这样维护还是很麻烦,因为这道题是对2^64取模的,我们可以 尝试把这个式子转换成只有加法和乘法的,这样就可以将其和前面

bzoj 2482: [Spoj GSS2] Can you answer these queries II 线段树

2482: [Spoj1557] Can you answer these queries II Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 145  Solved: 76[Submit][Status][Discuss] Description 给定n个元素的序列. 给出m个询问:求l[i]~r[i]的最大子段和(可选空子段). 这个最大子段和有点特殊:一个数字在一段中出现了两次只算一次. 比如:1,2,3,2,2,2出现了3次,但只算一次,