传送

感谢洛谷题解让我理清了这一撮标记

这里多了一个乘法操作,乘法的优先级高于加法。我们来思考一下有关标记的问题。

首先由两种操作,可以想到要有两个标记,一个标记乘法(mul[k]),一个标记加法(add[k])。

如果这一步是加法,就直接在原来的add上面增加即可(加法不会对mul产生影响)(这里的“原来的add”是指已经处理好加法,乘法关系的add),sum也按照线段树1的方式维护。

如果这一步是乘法,因为乘法的优先级高于加法,所以乘法会对当前的add产生影响,即add[k]要乘当前的数。mul[k],sum[k]直接乘当前的数即可。

再考虑一下标记下传。

子节点收到父亲节点的add和mul之后,我们就要考虑是先用父亲的add还是mul去更新子节点(即先乘再加还是先加再乘)。我们上面处理add的时候,就已经考虑到了mul对add的影响,如果这时候先加再乘,就会造成翻倍的错误,所以我们先乘再加(也就是先让子节点的add乘父节点的mul,再加上父节点的add)。sum[子节点]要先乘mul[父节点],再加上add[父节点]*(r-l+1)。

理清了两种标记之间的关系,剩下的就是线段树板子了。这里要注意建树的时候,mul[k]初始值为1,add[k]初始值为0。

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=;
ll n,m,p;
ll val[N*],sum[N*],add[N*],mul[N*];
ll read()
{
char ch=getchar();
ll x=;bool f=;
while(ch<''||ch>'')
{
if(ch=='-')f=;
ch=getchar();
}
while(ch>=''&&ch<='')
{
x=(x<<)+(x<<)+(ch^);
ch=getchar();
}
if(f)x=-x;
return x;
}
void build(ll k,ll l,ll r)
{
mul[k]=;
if(l==r)
{
sum[k]=val[r];
return ;
}
ll mid=(l+r)>>;
build(k<<,l,mid);
build(k<<|,mid+,r);
sum[k]=(sum[k<<]+sum[k<<|]+p)%p;
return;
}
void change(ll k,ll l,ll r,ll u)
{
add[k]=(mul[u]*add[k]%p+add[u])%p;//记得随时%p
mul[k]=(mul[k]*mul[u])%p;
sum[k]=(sum[k]*mul[u]%p+add[u]*(r-l+)%p)%p;
}
void pushdown(ll k,ll l,ll r)//标记下传
{
ll mid=(l+r)>>;
change(k<<,l,mid,k);
change(k<<|,mid+,r,k);
add[k]=;
mul[k]=;
return;
}
void Mul(ll k,ll l,ll r,ll x,ll y,ll v)
{
if(l>=x&&r<=y)
{
mul[k]=(mul[k]*v)%p;
add[k]=(add[k]*v)%p;
sum[k]=(sum[k]*v)%p;
return ;
}
pushdown(k,l,r);
ll mid=(l+r)>>;
if(x<=mid)
Mul(k<<,l,mid,x,y,v);
if(mid<y)
Mul(k<<|,mid+,r,x,y,v);
sum[k]=(sum[k<<]+sum[k<<|]+p)%p;
return;
}
void Add(ll k,ll l,ll r,ll x,ll y,ll v)
{
if(l>=x&&r<=y)
{
add[k]=add[k]+v;
sum[k]=(sum[k]+v*(r-l+)%p)%p;
return ;
} pushdown(k,l,r);
ll mid=(l+r)>>;
if(x<=mid)
Add(k<<,l,mid,x,y,v);
if(mid<y)
Add(k<<|,mid+,r,x,y,v);
sum[k]=(sum[k<<]+sum[k<<|]+p)%p;
return ;
}
ll query(ll k,ll l,ll r,ll x,ll y)
{
if(l>=x&&r<=y)
return sum[k]%p;
pushdown(k,l,r);
ll mid=(l+r)>>;
ll ans=;
if(x<=mid)
ans+=query(k<<,l,mid,x,y);
if(mid<y)
ans+=query(k<<|,mid+,r,x,y);
return (ans+p)%p; //防止出现负数
}
int main()
{
n=read();m=read();p=read();
for(int i=;i<=n;i++)
val[i]=read()%p;
build(,,n);
for(int i=;i<=N*;i++)
mul[i]=;
for(int i=;i<=m;i++)
{
ll cz,x,y;
cz=read();x=read();y=read();
if(cz==)
{
ll k=read();
Mul(,,n,x,y,k);
}
if(cz==)
{
ll k=read();
Add(,,n,x,y,k);
}
if(cz==)
{
printf("%lld\n",query(,,n,x,y));
}
}
}

最新文章

  1. c语言完成宽带拨号
  2. Mac OS X下重启apache
  3. Winform button按钮设置快捷键
  4. AMD加载器实现笔记(五)
  5. MD5加密操作
  6. Android中常用的布局
  7. 【Prince2科普】Prince2七大主题之概论
  8. 【转】Java中如何遍历Map对
  9. 【HDU 5184】 Brackets (卡特兰数)
  10. hdu--1316--How Many Fibs?(java大数)
  11. 201521123106 《Java程序设计》第7周学习总结
  12. [HAOI2015]数字串拆分
  13. 服务器、IP地址和域名之间有什么关系?
  14. Python学习过程中各个难点---函数篇
  15. 行为驱动开发(BDD) - 深入了解
  16. linux 权限管理 初识
  17. pygame-KidsCanCode系列jumpy-part12-platform图片
  18. C# 队列(Queue)解决简单并发
  19. stenciljs 学习八 组件测试
  20. tkinter入门,canvas实现百度,抖音,加载

热门文章

  1. 为什么说 Babel 将推动 JavaScript 的发展【转】
  2. dom元素的自上而下自动滚动
  3. 配置Trunk接口
  4. [19/05/13-星期一] HTML_head标签 和 body标签_文本标签
  5. winCE/Windows 应用程序消息提示框自动消失功能
  6. IDEA创建SpringBoot+maven项目
  7. [BZOJ1492] [NOI2007] 货币兑换Cash(cdq分治+斜率优化)
  8. selenium安装及环境搭建
  9. ubuntu18+virtualenv配置
  10. 2019 Multi-University Training Contest 7 - 1006 - Snowy Smile - 线段树