线段树 - AHOI 2009 维护序列 - Gym 237040G
老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。
有长为 n 的数列,不妨设为 a1,a2,⋯,an。有如下三种操作形式:
把数列中的一段数全部乘一个值;
把数列中的一段数全部加一个值;
询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模 P 的值。
第一行两个整数 n 和 P;
第二行含有 n 个非负整数,从左到右依次为 a1,a2,⋯,an;
第三行有一个整数 M,表示操作总数;
从第四行开始每行描述一个操作,输入的操作有以下三种形式:
操
作
1
:
"
1
t
g
c
"
,
表
示
把
所
有
满
足
t
≤
i
≤
g
的
a
i
改
为
a
i
×
c
;
操作 1:"1 \ t \ g \ c",表示把所有满足 t≤i≤g 的 a_i 改为 a_i×c;
操作1:"1 t g c",表示把所有满足t≤i≤g的ai改为ai×c;
操
作
2
:
"
2
t
g
c
"
,
表
示
把
所
有
满
足
t
≤
i
≤
g
的
a
i
改
为
a
i
+
c
;
操作 2:"2 \ t \ g \ c",表示把所有满足 t≤i≤g 的 a_i 改为 a_i+c;
操作2:"2 t g c",表示把所有满足t≤i≤g的ai改为ai+c;
操
作
3
:
"
3
t
g
"
,
询
问
所
有
满
足
t
≤
i
≤
g
的
a
i
的
和
模
P
的
值
。
操作 3:"3 \ t \ g",询问所有满足 t≤i≤g 的 a_i 的和模 P 的值。
操作3:"3 t g",询问所有满足t≤i≤g的ai的和模P的值。
同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。
Output
对每个操作 3,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。
Example
Input
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
Output
2
35
8
对 于 全 部 测 试 数 据 , 1 ≤ t ≤ g ≤ n , 0 ≤ c , a i ≤ 1 0 9 , 1 ≤ P ≤ 1 0 9 。 对于全部测试数据,1≤t≤g≤n,0≤c,a_i≤10^9,1≤P≤10^9。 对于全部测试数据,1≤t≤g≤n,0≤c,ai≤109,1≤P≤109。
分析:
由 于 存 在 区 间 乘 和 区 间 加 两 种 操 作 , 因 此 需 在 节 点 结 构 体 中 增 加 两 个 懒 标 记 m u l 和 a d d 。 由于存在区间乘和区间加两种操作,因此需在节点结构体中增加两个懒标记mul和add。 由于存在区间乘和区间加两种操作,因此需在节点结构体中增加两个懒标记mul和add。
由 于 用 到 两 个 懒 标 记 , 在 做 修 改 操 作 时 , 为 了 代 码 的 简 洁 性 , 用 一 个 操 作 来 实 现 懒 标 记 m u l 和 a d d 的 修 改 。 由于用到两个懒标记,在做修改操作时,为了代码的简洁性,用一个操作来实现懒标记mul和add的修改。 由于用到两个懒标记,在做修改操作时,为了代码的简洁性,用一个操作来实现懒标记mul和add的修改。
为 了 方 便 代 码 实 现 , 我 们 规 定 先 进 行 乘 法 操 作 , 再 进 行 加 法 操 作 。 即 将 每 个 数 表 示 成 : a × b + c 的 形 式 。 为了方便代码实现,我们规定先进行乘法操作,再进行加法操作。即将每个数表示成:a×b+c的形式。 为了方便代码实现,我们规定先进行乘法操作,再进行加法操作。即将每个数表示成:a×b+c的形式。
修 改 前 , 初 始 值 为 a , b = 1 , c = 0 , 修 改 后 得 到 ( a × b + c ) × d + e 。 修改前,初始值为a,b=1,c=0,修改后得到(a×b+c)×d+e。 修改前,初始值为a,b=1,c=0,修改后得到(a×b+c)×d+e。
若 进 行 乘 法 操 作 , 则 e = 0 , 扩 大 d 倍 ; 若 进 行 加 法 操 作 , 则 d = 1 , 增 加 e 。 若进行乘法操作,则e=0,扩大d倍;若进行加法操作,则d=1,增加e。 若进行乘法操作,则e=0,扩大d倍;若进行加法操作,则d=1,增加e。
( a × b + c ) × d + e = ( a × b ) × d + ( c × d + e ) , 对 比 修 改 前 a × b + c , (a×b+c)×d+e=(a×b)×d+(c×d+e),对比修改前a×b+c, (a×b+c)×d+e=(a×b)×d+(c×d+e),对比修改前a×b+c,
懒 标 记 m u l i = m u l i − 1 × m u l , a d d i = a d d i − 1 × m u l + a d d , 其 中 m u l 和 a d d 是 需 要 修 改 的 具 体 数 值 。 懒标记mul_{i}=mul_{i-1}×mul,add_i=add_{i-1}×mul+add,其中mul和add是需要修改的具体数值。 懒标记muli=muli−1×mul,addi=addi−1×mul+add,其中mul和add是需要修改的具体数值。
记 父 节 点 为 u , 左 右 孩 子 分 别 为 l e f t 和 r i g h t , 则 懒 标 记 的 传 递 过 程 为 : 记父节点为u,左右孩子分别为left和right,则懒标记的传递过程为: 记父节点为u,左右孩子分别为left和right,则懒标记的传递过程为:
① 、 l e f t . s u m = l e f t . s u m × u . m u l + ( l e f t . r − l e f t . l + 1 ) × u . a d d ①、left.sum=left.sum×u.mul+(left.r-left.l+1)×u.add ①、left.sum=left.sum×u.mul+(left.r−left.l+1)×u.add
② 、 l e f t . m u l = l e f t . m u l × u . m u l ②、left.mul=left.mul×u.mul ②、left.mul=left.mul×u.mul
③ 、 l e f t . a d d = l e f t . a d d × u . m u l + u . a d d ③、left.add=left.add×u.mul+u.add ③、left.add=left.add×u.mul+u.add
其 中 , s u m 是 指 当 前 节 点 所 代 表 的 区 间 和 。 右 孩 子 r i g h t 同 理 。 其中,sum是指当前节点所代表的区间和。右孩子right同理。 其中,sum是指当前节点所代表的区间和。右孩子right同理。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e5+10;
struct node
{
int l,r;
int sum,mul,add;
}tr[N*4];
int n,p,m;
int w[N];
// (a*b+c)*d+e = a*b*d + c*d+e
void cal(node &t,int mul,int add)
{
t.sum=((ll)t.sum*mul+(ll)(t.r-t.l+1)*add)%p;
t.mul=(ll)t.mul*mul%p;
t.add=((ll)t.add*mul+add)%p;
}
void pushup(int u)
{
tr[u].sum=(tr[u<<1].sum+tr[u<<1|1].sum)%p;
}
void pushdown(int u)
{
cal(tr[u<<1],tr[u].mul,tr[u].add);
cal(tr[u<<1|1],tr[u].mul,tr[u].add);
tr[u].add=0,tr[u].mul=1; //清空父节点懒标记
}
void build(int u,int l,int r)
{
if(l==r) tr[u]={l,r,w[r],1,0};
else
{
tr[u]={l,r,0,1,0};
int mid=l+r>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
}
void modify(int u,int l,int r,int mul,int add)
{
if(l<=tr[u].l&&tr[u].r<=r) cal(tr[u],mul,add);
else
{
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid) modify(u<<1,l,r,mul,add);
if(r>mid) modify(u<<1|1,l,r,mul,add);
pushup(u);
}
}
int query(int u,int l,int r)
{
if(l<=tr[u].l&&tr[u].r<=r) return tr[u].sum;
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
int res=0;
if(l<=mid) res=query(u<<1,l,r);
if(r>mid) res=(res+query(u<<1|1,l,r))%p;
return res;
}
int main()
{
scanf("%d%d",&n,&p);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
build(1,1,n);
scanf("%d",&m);
int op,l,r,d;
while(m--)
{
scanf("%d%d%d",&op,&l,&r);
if(op==1)
{
scanf("%d",&d);
modify(1,l,r,d,0);
}
else if(op==2)
{
scanf("%d",&d);
modify(1,l,r,1,d);
}
else printf("%d\n",query(1,l,r));
}
return 0;
}