[AHOI2009] 行星序列 - 线段树双懒标记

本文介绍了一道涉及模拟飞行中行星质量变化及求和的复杂算法题目。通过构建支持懒惰传播的线段树来高效处理质量变化操作,包括乘法和加法更新,以及求指定范围内行星质量总和。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


题目描述

“神州“载人飞船的发射成功让小可可非常激动,他立志长大后要成为一名宇航员假期一始,他就报名参加了“小小宇航员夏令营”,在这里小可可不仅学到了丰富的宇航知识,还参与解决了一些模拟飞行中发现的问题,今天指导老师交给他一个任务,在这次模拟飞行的路线上有N个行星,暂且称它们为一个行星序列,并将他们从1至n标号,在宇宙未知力量的作用下这N个行星的质量是不断变化的,所以他们对飞船产生的引力也会不断变化,小可可的任务就是在飞行途中计算这个行星序列中某段行星的质量和,以便能及时修正飞船的飞行线路,最终到达目的地,行星序列质量变化有两种形式:
  1.行星序列中某一段行星的质量全部乘以一个值
  2.行星序列中某一段行星的质量全部加上一个值
由于行星的质量和很大,所以求出某段行星的质量和后只要输出这个值模P的结果即可,小可可被这个任务难住了,聪明的你能够帮他完成这个任务吗?


输入格式

第一行两个整数N和P(1<=p<=1000000000);
第二行含有N个非负整数,从左到右依次为a1,a2,………,an(0<=ai<=100000000,1<=i<=n),其中ai表示第i个行星的质量:
第三行有一个整数m,表示模拟行星质量变化以及求质量和等操作的总次数。从第四行开始每行描述一个操作,输入的操作有以下三种形式:
操作1:1 t g c 表示把所有满足t<=i<=g的行星质量ai改为ai*c
操作2:2 t g c 表示把所有满足t<=i<=g的行星质量ai改为ai+c
操作3:3 t g 表示输出所有满足t<=i<=g的ai的和模p的值
其中:1<=t<=g<=N,0<=c<=10000000
注:同一行相邻的两数之间用一个空格隔开,每行开头和末尾没有多余空格


输出格式

对每个操作3,按照它在输入中出现的顺序,依次一行输出一个整数表示所求行星质量和


样例数据

样例输入

7 43
1 2 3 4 5 6 7
5
1 2 5 5
3 2 4
2 3 7 9
3 1 3
3 4 7

样例说明

2
35
8

样例输出

初始时数列为(1,2,3,4,5,6,7)。
经过第1次操作后,数列为(1,10,15,20,25,6,7)。
对第2次操作,和为10+15+20=45,模43的结果是2。
经过第3次操作后,数列为(1,10,24,29,34,15,16)
对第4次操作,和为1+10+24=35,模43的结果是35。
对第5次操作,和为29+34+15+16=94,模43的结果是8。


数据规模

40%的数据中,M,N<=10000
100%的数据中,M,N<=100000


题目分析

挺难的一道题
因为有两个操作:+和*
考虑如何让这两个操作兼容
一个标记是肯定不行的了
因此引入两个懒标记分别统计尚未下传的*的数和尚未下传的+的数
考虑双标记如何互相影响
比如这样一棵树
这里写图片描述
现在我给[1,2]*5,标记尚未下传
这里写图片描述
接着标记上传
这里写图片描述
现在再给[1,2]+8,标记还是没有下传
这里写图片描述
为什么sum是31而不是23呢?
因为2有两个子结点,这里的+8代表子结点都+8,因此要+16
标记上传
这里写图片描述
假设再给[1,2]*6,关键的部分来了
这里写图片描述
加法标记要乘6,乘法标记也要乘6
为什么会这样呢?这是由乘法的分配率决定的
(a*b+c)*d=a*b*d+c*d
现在我查询[1,3]的和,答案即为186+3=189
再查询[2,3]的和,
[2,3]将[1,2]分开了,因此要标记下传
标记下传时同时要合并儿子结点的标记
先将儿子结点的mullazy乘上父亲的mullazy
再将儿子结点的pluslazy乘上父亲的mullazy再加pluslazy
接着计算儿子结点的sum
这里写图片描述
那么[2,3]的和即为108+3=111

此题完美解决了,实现代码时要注意先后顺序以及标记清零。


源代码

#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
typedef long long LL;
inline const LL Get_Int() {
    LL num=0,bj=1;
    char x=getchar();
    while(x<'0'||x>'9') {
        if(x=='-')bj=-1;
        x=getchar();
    }
    while(x>='0'&&x<='9') {
        num=num*10+x-'0';
        x=getchar();
    }
    return num*bj;
}
LL n,mod;
const int maxn=100000;
struct Tree { //修改区间 查询区间(+双懒标记)
    LL left,right,mullazy,pluslazy,sum; //sum表示质量和 mullazy乘积懒标记 pluslazy加法懒标记 
};
struct Segment_Tree { //双标记维护和 
    Tree tree[maxn*4];
    LL sum;
    void build(int index,int Left,int Right) {
        tree[index].left=Left;
        tree[index].right=Right;
        tree[index].sum=0;
        tree[index].mullazy=1;
        tree[index].pluslazy=0;
        if(Left==Right)return;
        int mid=(Left+Right)/2;
        build(2*index,Left,mid);
        build(2*index+1,mid+1,Right);
    }
    void push_down(int index) { //标记下传,下传当前lazy(标记为0不下传)
        tree[index*2].mullazy=(tree[index*2].mullazy*tree[index].mullazy)%mod;
        tree[index*2].pluslazy=(tree[index*2].pluslazy*tree[index].mullazy)%mod;
        tree[index*2].pluslazy=(tree[index*2].pluslazy+tree[index].pluslazy)%mod;
        tree[index*2].sum=(tree[index*2].sum*tree[index].mullazy)%mod;
        tree[index*2].sum=(tree[index*2].sum+tree[index].pluslazy*(tree[index*2].right-tree[index*2].left+1))%mod;
        tree[index*2+1].mullazy=(tree[index*2+1].mullazy*tree[index].mullazy)%mod;
        tree[index*2+1].pluslazy=(tree[index*2+1].pluslazy*tree[index].mullazy)%mod;
        tree[index*2+1].pluslazy=(tree[index*2+1].pluslazy+tree[index].pluslazy)%mod;
        tree[index*2+1].sum=(tree[index*2+1].sum*tree[index].mullazy)%mod;
        tree[index*2+1].sum=(tree[index*2+1].sum+tree[index].pluslazy*(tree[index*2+1].right-tree[index*2+1].left+1))%mod;
        tree[index].mullazy=1;
        tree[index].pluslazy=0;
    }
    void push_up(int index) { //标记上传,合并子树信息
        tree[index].sum=(tree[index*2].sum+tree[index*2+1].sum)%mod;
    }
    void modify(int index,int Left,int Right,int type,LL data) { //type->1 + type->2 *
        if(Right<tree[index].left||Left>tree[index].right)return; //不相交
        if(Left<=tree[index].left&&Right>=tree[index].right) { //完全包含
            if(type==1) {
                tree[index].pluslazy=(tree[index].pluslazy+data)%mod;
                tree[index].sum=(tree[index].sum+(tree[index].right-tree[index].left+1)*data)%mod;
            } else {
                tree[index].mullazy=(tree[index].mullazy*data)%mod;
                tree[index].pluslazy=(tree[index].pluslazy*data)%mod;
                tree[index].sum=(tree[index].sum*data)%mod;
            }
            return;
        }
        push_down(index); //标记下传
        modify(index*2,Left,Right,type,data);
        modify(index*2+1,Left,Right,type,data);
        push_up(index); //标记上传
    }
    void init() {
        sum=0;
    }
    void Query(int index,int Left,int Right) {
        if(Right<tree[index].left||Left>tree[index].right)return; //不相交
        if(Left<=tree[index].left&&Right>=tree[index].right) { //完全包含
            sum=(sum+tree[index].sum)%mod;
            return;
        }
        push_down(index);
        Query(index*2,Left,Right);
        Query(index*2+1,Left,Right);
        push_up(index);
    }
};
Segment_Tree st;
LL m;
int main() {
    n=Get_Int();
    mod=Get_Int();
    st.build(1,1,n);
    for(int i=1; i<=n; i++)st.modify(1,i,i,1,Get_Int());
    m=Get_Int();
    for(int i=1; i<=m; i++) {
        int order=Get_Int();
        if(order==1||order==2) {
            order=order%2+1;
            int Left=Get_Int(),Right=Get_Int(),data=Get_Int();
            st.modify(1,Left,Right,order,data);
        } else {
            int Left=Get_Int(),Right=Get_Int();
            st.init();
            st.Query(1,Left,Right);
            printf("%lld\n",st.sum);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值