Luogu P3373 【模板】线段树 2
这还是比较简单的线段树模板题吧
更多的是给一个思路,当维护多种需要Lazy Tag的操作时顺序问题
主要是当标记下传的时候,要注意先处理乘法在处理加法
Why?
因为显然乘法的优先级比加法高,如果先加后乘就会爆掉
例如:10+6*233
如果先算加法那么就是16*233,那么显然是错的
而且就算是这样的情况如(10+100)*233
也可以把乘法标记先传给加法化成10*233+100*233(乘法分配律的原理,小学数学)
然后规定好顺序就可以直接套线段树的板子了
CODE
#include<cstdio>
using namespace std;
typedef long long LL;
const LL N=100005;
struct segtree
{
LL sum,add,mul;
}tree[N<<2];
LL n,m,p,a[N],opt,x,y,z;
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(LL &x)
{
x=0; char ch=tc();
while (ch<'0'||ch>'9') ch=tc();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
inline void write(LL x)
{
if (x/10) write(x/10);
putchar(x%10+'0');
}
inline void up(LL root)
{
tree[root].sum=(tree[root<<1].sum+tree[root<<1|1].sum)%p;
}
inline void down(LL root,LL l,LL r)
{
if (tree[root].add||tree[root].mul!=1)
{
tree[root<<1].mul=(tree[root<<1].mul*tree[root].mul)%p;
tree[root<<1|1].mul=(tree[root<<1|1].mul*tree[root].mul)%p;
tree[root<<1].add=(tree[root<<1].add*tree[root].mul)%p;
tree[root<<1|1].add=(tree[root<<1|1].add*tree[root].mul)%p;
tree[root<<1].sum=(tree[root<<1].sum*tree[root].mul)%p;
tree[root<<1|1].sum=(tree[root<<1|1].sum*tree[root].mul)%p;
tree[root].mul=1;
tree[root<<1].add=(tree[root<<1].add+tree[root].add)%p;
tree[root<<1|1].add=(tree[root<<1|1].add+tree[root].add)%p;
tree[root<<1].sum=(tree[root<<1].sum+tree[root].add*l)%p;
tree[root<<1|1].sum=(tree[root<<1|1].sum+tree[root].add*r)%p;
tree[root].add=0;
}
}
inline void build(LL root,LL l,LL r)
{
tree[root].mul=1;
if (l==r)
{
tree[root].sum=a[l];
return;
}
LL mid=l+r>>1;
build(root<<1,l,mid); build(root<<1|1,mid+1,r);
up(root);
}
inline void modify_mul(LL root,LL l,LL r,LL beg,LL end,LL k)
{
if (l>=beg&&r<=end)
{
tree[root].mul=(tree[root].mul*k)%p;
tree[root].add=(tree[root].add*k)%p;
tree[root].sum=(tree[root].sum*k)%p;
return;
}
LL mid=l+r>>1;
down(root,mid-l+1,r-mid);
if (beg<=mid) modify_mul(root<<1,l,mid,beg,end,k);
if (mid<end) modify_mul(root<<1|1,mid+1,r,beg,end,k);
up(root);
}
inline void modify_add(LL root,LL l,LL r,LL beg,LL end,LL k)
{
if (l>=beg&&r<=end)
{
tree[root].add=(tree[root].add+k)%p;
tree[root].sum=(tree[root].sum+k*(r-l+1))%p;
return;
}
LL mid=l+r>>1;
down(root,mid-l+1,r-mid);
if (beg<=mid) modify_add(root<<1,l,mid,beg,end,k);
if (mid<end) modify_add(root<<1|1,mid+1,r,beg,end,k);
up(root);
}
inline LL query(LL root,LL l,LL r,LL beg,LL end)
{
if (l>=beg&&r<=end) return tree[root].sum;
LL mid=l+r>>1,res=0;
down(root,mid-l+1,r-mid);
if (beg<=mid) res=(res+query(root<<1,l,mid,beg,end))%p;
if (mid<end) res=(res+query(root<<1|1,mid+1,r,beg,end))%p;
up(root);
return res;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
register LL i;
read(n); read(m); read(p);
for (i=1;i<=n;++i)
read(a[i]);
build(1,1,n);
while (m--)
{
read(opt); read(x); read(y);
if (opt==1) read(z),modify_mul(1,1,n,x,y,z%p);
if (opt==2) read(z),modify_add(1,1,n,x,y,z%p);
if (opt==3) write(query(1,1,n,x,y)),putchar('\n');
}
return 0;
}
此题和这道题基本类似完全一样,双倍经验即可(但我打了两次,权当熟练,毕竟线段树真的很基础也重要)
辣鸡老年选手AFO在即