题解 P3373 【【模板】线段树 2】
离子键Ionic_Bond · · 题解
来篇清新的题解
我感觉楼上各位
线段树这种简洁而又明了的数据结构,显然还是用数组来存树和tag比较好啊qwq
数组介绍
解法介绍:
我们参考8848的广告
A.根据乘法和加法的优先级不同,所以传
B.在计算过程中应当随时保持加法
C.较长代码的小技巧:尽量把取值、传值等分支操作加到小函数里,当写但是不压行会增大常数(很好与优秀只差一点点距离,这段距离,叫码风)
代码解析(已AC)
//相信我,位运算真的会快!!真的不是装B!!
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define il inline
#define Maxn 100100
using namespace std;
ll tr[Maxn<<2],mag[Maxn<<2],aag[Maxn<<2],leaf[Maxn],n,m,md;
ll Q;
ll s,e,z;
ll ans;
ll ls(ll p)
{
return p<<1;
}//取左儿子
ll rs(ll p)
{
return p<<1|1;
}//取右儿子
il void pushup(ll p)
{
tr[p]=(tr[ls(p)]+tr[rs(p)])%md;
}//带模数的向上传值
il void pushup1(ll p)
{
tr[p]=tr[ls(p)]+tr[rs(p)];
}//建树专用,不带模数的向上传值
void build(ll p,ll l,ll r)
{
mag[p]=1;
ll m=(l+r)>>1;
if(l==r)
{
tr[p]=leaf[l];//tr[p]=leaf[r];
return;
}
build(ls(p),l,m);
build(rs(p),m+1,r);
pushup1(p);
tr[p]%=md;
return;
}//建树,有了取子函数非常简洁qwq
il void pushdown(ll p,ll l,ll r)
{
ll m=(l+r)>>1;
tr[ls(p)]=(tr[ls(p)]*mag[p]+aag[p]*(m-l+1))%md;//向左儿子传结果
tr[rs(p)]=(tr[(rs(p))]*mag[p]+aag[p]*(r-m))%md;//向右儿子传结果
mag[ls(p)]=(mag[ls(p)]*mag[p])%md;//向左儿子传乘法tag
mag[rs(p)]=(mag[rs(p)]*mag[p])%md;//向右儿子传乘法tag
aag[ls(p)]=(aag[ls(p)]*mag[p]+aag[p])%md;//向左儿子传加法tag
aag[rs(p)]=(aag[rs(p)]*mag[p]+aag[p])%md;//向右儿子传加法tag
mag[p]=1;
aag[p]=0;
}//唯一一个略显复杂的函数,向下传tag
il void updmul(ll p,ll l,ll r,ll gl,ll gr,ll k)
{
if(gr<l||gl>r)return;//若不在此区间就返回上一层,下同
if(gl<=l&&gr>=r)
{
tr[p]=(tr[p]*k)%md;
mag[p]=(mag[p]*k)%md;
aag[p]=(aag[p]*k)%md;
return;
}
pushdown(p,l,r);
ll m=(l+r)>>1;
updmul(ls(p),l,m,gl,gr,k);
updmul(rs(p),m+1,r,gl,gr,k);
pushup(p);
}//常规操作,区间乘法
il void updadd(ll p,ll l,ll r,ll gl,ll gr,ll k)
{
if(gl>r||gr<l)return;
if(gl<=l&&gr>=r)
{
tr[p]=(tr[p]+(r-l+1)*k)%md;
aag[p]=(aag[p]+k)%md;
return;
}
pushdown(p,l,r);
ll m=(l+r)>>1;
updadd(ls(p),l,m,gl,gr,k);
updadd(rs(p),m+1,r,gl,gr,k);
pushup(p);
}//常规操作,区间加法
ll Question(ll p,ll l,ll r,ll gl,ll gr)
{
if(gl>r||gr<l)return 0;
if(gl<=l&&gr>=r)
{
return tr[p];
}
pushdown(p,l,r);
ll m=(l+r)>>1;
return (Question(ls(p),l,m,gl,gr)+Question(rs(p),m+1,r,gl,gr))%md;
}//个人喜欢用Question,也可以用query,区间查询
int main()
{
scanf("%lld%lld%lld",&n,&m,&md);
for(int i=1;i<=n;i++)
{
scanf("%lld",&leaf[i]);
}
build(1,1,n);
for(int i=1;i<=m;i++)
{
scanf("%lld",&Q);
if(Q==1)
{
scanf("%lld%lld%lld",&s,&e,&z);
updmul(1,1,n,s,e,z);
}
if(Q==2)
{
scanf("%lld%lld%lld",&s,&e,&z);
updadd(1,1,n,s,e,z);
}
if(Q==3)
{
scanf("%lld%lld",&s,&e);
printf("%lld\n",Question(1,1,n,s,e));
}
}
return ~~(0-0);//(求过)
}
我的这篇题解虽然解法普通,但是我相信这篇题解更容易阅读和理解,所以请管理员欧尼桑们过一下吧qwq(~-△-~)