题目链接:https://www.luogu.com.cn/problem/P2023
分析:
线段树的节点:
struct Node
{
int l,r;
int add; // 懒标记 加法
int mul; // 懒标记 乘法
int sum; // 真正要求的值
}
1.如何通过属性更新sum值。
,如何将sum 的值通过 add 和 mul进行更新,和之前的线段树中的“你能回答这些问题吗”的题目构造节点属性的思想类似,你能回答这些问题嘛的题解链接 , 在这道题中为了能够算出对应的值,添加了许多属性。
如何通过add,mul属性得到我们需要的sum属性呢?可以发现有这么一种维护方式:
( sum + add ) * mul 的方式, (也就是将每次的操作都转为存储add,mul,然后最后用这个方式计算) 这个方式的话 乘法修改很容易修改。 (sum + add) * mul * c.
所以只要将前mul 改为 mul * c即可。 但是加法呢?
(sum + add) * mul + d, 这个时候就会发现这样维护的方式无法通过子节点更新父结点的add懒标记属性值。
那么是否要向 “你能回答这些问题嘛”那道题目一样添加新的属性呢?
实际上是不用的,我们可以用另一种计算sum的方式
sum * mul + add 的方式,
乘法: (( sum * mul ) + add ) * m;
则mul改为 mul * m ,add改为 add * m
而加法: sum * mul + add + m , 所以add 改为add + m即可。
所以可以很容易的进行维护。
同时还有一个技巧,我们这道题目有两个修改操作,加法修改,乘法修改,
是否需要使用不同的函数对其进行操作呢?
实际上是不需要的,
sum * a 可以 转化为 sum * a + 0
sum + b 可以转化为 sum * 1 + b;
所以乘法修改和加法修改都可以改为 乘 和 加 合并修改。
最后,进行相应的操作
pushup(),修改父结点的sum值, root .sum = leftson.sum + right.sum
pushdown,
根据公式 (sum * a + b ) * c + d == sum * a * c + b * c + d;
所以一个根节点 和 add值 以及 mul值的修改
sum = sum * mul + (r - l + 1) * d;
mul = mul * c;
add = add * c + d
其他操作基本不变。
代码实现:
# include <iostream>
using namespace std;
const int N = 100010;
int a[N];
struct Node
{
int l,r;
int sum;
int add;
int mul;
}edgs[N * 4];
int n,m,p;
void pushup(int u)
{
edgs[u].sum = (edgs[2 * u].sum + edgs[2 * u +1].sum) % p;
}
void eval(int root , int d,int m) //将root节点更新新的add,mul等懒标记,以及新的sum值
{
edgs[root].sum = ( (long long)m * edgs[root].sum +(long long)(edgs[root].r - edgs[root].l +1 ) * d ) % p; // 需要注意到其中的区间加的修改整个区间都加add值
edgs[root].mul = (long long)edgs[root].mul * m % p;
edgs[root].add = ( (long long)edgs[root].add * m + d ) % p;
}
void pushdown(int u)
{
//将懒标记对应的值下放到子节点
eval(2 * u , edgs[u].add , edgs[u].mul);
eval(2 * u + 1 , edgs[u].add , edgs[u].mul);
// 清空根节点的懒标记操作
edgs[u].add = 0;
edgs[u].mul = 1;
}
void build(int u , int l , int r)
{
edgs[u].l = l, edgs[u].r = r, edgs[u].add = 0 , edgs[u].mul = 1;
if(l == r)
{
edgs[u].sum = a[l];
return;
}
else
{
int mid = ( edgs[u].l + edgs[u].r ) / 2;
build(2 * u , l , mid);
build(2 * u +1 , mid + 1 , r);
//而非叶子节点的edgs[u].sum的属性值是由pushup产生的。所以它们是否赋值都可以
pushup(u);
}
}
void modify(int u , int l , int r , int add , int mul)
{
if(edgs[u].l >= l && edgs[u].r <= r)
{
eval(u,add,mul);
return;
}
else
{
pushdown(u);
int mid = ( edgs[u].l + edgs[u].r ) / 2;
if(l <= mid)
{
modify(2 * u ,l , r , add , mul);
}
if(r > mid)
{
modify(2 * u + 1 , l , r, add , mul);
}
pushup(u);
}
}
int query(int u , int l ,int r)
{
if(edgs[u].l >= l && edgs[u].r <= r)
{
return edgs[u].sum;
}
else
{
pushdown(u);
int mid = ( edgs[u].l + edgs[u].r ) / 2;
int sum = 0;
if(l <= mid)
{
sum = query(2 * u , l , r) % p;
}
if(r > mid)
{
sum =( (long long)sum + query(2 * u +1 , l , r) ) % p;
}
return sum;
}
}
int main()
{
scanf("%d %d",&n,&p);
for(int i = 1 ; i <= n ; i++)
{
scanf("%d",&a[i]);
}
build(1,1,n);
scanf("%d",&m);
while(m--)
{
int a;
scanf("%d",&a);;
if(a == 1)
{
int t ,g,c;
scanf("%d %d %d",&t,&g,&c);
modify(1,t,g,0,c); // 乘法操作,所以add == 0 , mul = c
}
else if(a == 2)
{
int t ,g,c;
scanf("%d %d %d",&t,&g,&c);
modify(1,t,g,c,1); // 加法操作,所以add == c , mul = 1
}
else
{
int t,g;
scanf("%d %d",&t,&g);
printf("%d\n",query(1,t,g));
}
}
return 0;
}