传送

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

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

首先由两种操作,可以想到要有两个标记,一个标记乘法(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));
}
}
}

线段树2(P3373)的更多相关文章

  1. [线段树]Luogu P3373 【模板】线段树 2

    #include<cstdio> #include<cstring> #include<algorithm> #define R register #define ...

  2. 洛谷P3373 【模板】线段树 2

     P3373 [模板]线段树 2 47通过 186提交 题目提供者HansBug 标签 难度提高+/省选- 提交  讨论  题解 最新讨论 为啥WA(TAT) 题目描述 如题,已知一个数列,你需要进行 ...

  3. 洛谷 P3373 【模板】线段树 2 解题报告

    P3373 [模板]线段树 2 题目描述 如题,已知一个数列,你需要进行下面三种操作: 1.将某区间每一个数乘上\(x\) 2.将某区间每一个数加上\(x\) 3.求出某区间每一个数的和 输入输出格式 ...

  4. 洛谷P3373线段树模板2

    题目:https://www.luogu.org/problemnew/show/P3373 带乘的线段树,更新时把加的标记也乘一下,然后取值时先乘后加. 代码如下: #include<iost ...

  5. AC日记——【模板】线段树 2 洛谷 P3373

    P3373 [模板]线段树 2387通过1.8K提交标签难度 提高+/省选- 提交 讨论 题解 最新讨论 更多讨论 2333最后三个点卡常数.迷之RE感觉这题很迷啊好像一共三组测试数据.友情提示:开l ...

  6. 洛谷——P3373 【模板】线段树 2&& B 数据结构

    P3373 [模板]线段树 2 题目描述 如题,已知一个数列,你需要进行下面三种操作: 1.将某区间每一个数乘上x 2.将某区间每一个数加上x 3.求出某区间每一个数的和 线段树维护区间乘法 1.如何 ...

  7. 洛谷 P3373 【模板】线段树 2

    洛谷 P3373 [模板]线段树 2 洛谷传送门 题目描述 如题,已知一个数列,你需要进行下面三种操作: 将某区间每一个数乘上 xx 将某区间每一个数加上 xx 求出某区间每一个数的和 输入格式 第一 ...

  8. 洛谷 P3373 【模板】线段树 2 题解

    P3373 [模板]线段树 2 题目描述 如题,已知一个数列,你需要进行下面三种操作: 1.将某区间每一个数乘上x 2.将某区间每一个数加上x 3.求出某区间每一个数的和 输入格式 第一行包含三个整数 ...

  9. 【原创】洛谷 LUOGU P3373 【模板】线段树2

    P3373 [模板]线段树 2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格式: 第 ...

  10. P3373 【模板】线段树 2 (未完待续)

    P3373 [模板]线段树 2 强烈安利这个大佬 超赞!!! 题解 本来以为这个题拿着线段树1的板子改改就好了,但是发现事情并没有那么简单,改了两天... 我们看到这个题其实涉及啦乘法和加法两种运算, ...

随机推荐

  1. 在Window平台是模拟Linux的Shell环境

    在Linux平台模拟Linux的shell环境,可以通过一个软件实现:Cygwin(点击进入官网下载好即可),如下图(选择对应的版本进行下载): 安装: 1. 双击运行下载的安装包(选择从网络安装), ...

  2. linux中常用的60个命令及作用详解

    Linux 必学的 60 个命令 Linux 提供了大量的命令,利用它可以有效地完成大量的工作,如磁盘操作.文件存取.目录操作.进程管理.文件权限设定等.所以,在 Linux 系统上工作离不开使用系统 ...

  3. linux command --- terminal common commands

    switch to root : sudo su.su root.sudo -s switch to users : su god(user name) set root password : sud ...

  4. [Git] 008 status 与 commit 命令的补充

    本文的"剧情"承接 [Git] 007 三棵树以及向本地仓库加入第一个文件 1. 对 "status" 的补充 1.1 "status" 有 ...

  5. Oracle数据库控制台常用命令

    安装好数据库以后可以通过“Win+R”打开控制台,在控制台登录Oracle输入命令来操作数据库. SQLPlus命令: SQLPlus命令是用来登录Oracle数据库的命令,有两种写法,分别如下: ( ...

  6. NameVirtualHost的使用

    如果某个ip:port 上只设置了一个虚拟主机,那么NameVirtualHost ip:port 可以不写,因为从这个ip:port的访问不需要做任何选择:如上例中192.168.1.197只对应于 ...

  7. 有用的2个 Windows 下批处理文件(bat文件):

    创建多个文件夹(目录): 新建一个记事本:win + R –>输入notepad 在笔记本中输入如下: mkdir Would mkdir you mkdir marry mkdir me 另存 ...

  8. nodejs 模板引擎

    自制替换模板 template.js var fs = require('fs') var http = require('http') var server = http.createServer( ...

  9. How to Add Memory, vCPU, Hard Disk to Linux KVM Virtual Machine

    ref: https://www.thegeekstuff.com/2015/02/add-memory-cpu-disk-to-kvm-vm/ In our previous article of ...

  10. python面向对象--item方法

    class Foo: def __getitem__(self, item): print("getitem") return self.__dict__[item] def __ ...