线段树lazy标记:加乘混合
Posted wzx-rs-sthn
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树lazy标记:加乘混合相关的知识,希望对你有一定的参考价值。
题目
Description
给定一个正整数序列A,要求支持以下操作
1): + a b c 表示在[a,b]上加上一个常数C。
2): * a b c 在[a,b]上乘上一个常数K。
3): QUERY a b 查询[a,b]的sum。
Input
第一行两个正整数n、m,n表示序列长度,m表示操作数
第二行n个正整数,第i表示A[i]的大小
接下来的m行,每行有且仅有一种操作,具体和题目描述一致?
n,m<=100000
其他权值都<=50000
小心爆int
Output
对于每个询问操作,输出答案对1000000007取余的结果
Sample Input
10 10 50 14 20 18 19 11 43 43 26 44 + 3 6 41 QUERY 1 2 + 5 5 14 QUERY 1 3 QUERY 4 4 QUERY 2 6 * 3 5 31 * 4 8 20 * 5 8 28 QUERY 6 7
Sample Output
64 125 59 260 53200
思路:
这是一道很明显的线段树修改题;
写这题前你必须知道普通的lazy标记怎么写;
那么这题有什么不一样呢,很显然是要考虑加乘的顺序;
如(a+b)*c ,a为线段树某一区间和,b为这个区间的 加法lazy标记,c为要乘上的数;
那么就要 a*c+b*c ,而不是 a*c+b;
所以我们不仅需要 把a(也就是某一区间和)*c 还要把b(这个区间的加法lazy标记)*c;
当然除了下传加法标记,也要下传乘法标记;
所以这一题不一样的是只要在下传乘法标记的时候,把加法标记也乘个c就ok了
代码:
#include<bits/stdc++.h> typedef long long ll; using namespace std; inline ll read() { ll a=0,f=1; char c=getchar(); while (c<‘0‘||c>‘9‘) {if (c==‘-‘) f=-1; c=getchar();} while (c>=‘0‘&&c<=‘9‘) {a=a*10+c-‘0‘; c=getchar();} return a*f; } const ll mod=1000000007; ll n,m,aa[1000006]; struct sbbb { ll l,r,v,f,ff=1; }a[5000001]; inline ll R(ll x)//计算x的右节点的编号 { return x*2+1; } inline ll L(ll x)//计算x的左节点的编号 { return x*2; } inline void doit(ll p)//区间维护 { a[p].v=(a[L(p)].v+a[R(p)].v)%mod; } inline void build(ll p,ll l,ll r)//建树 { a[p].l=l;a[p].r=r; if(l==r) { a[p].v=aa[l]; return; } ll mid=(l+r)>>1; build(L(p),l,mid); build(R(p),mid+1,r); doit(p); } inline void push_down(ll p)//标记下传 { ll d=a[p].ff; a[L(p)].v=(a[L(p)].v*d)%mod; a[R(p)].v=(a[R(p)].v*d)%mod;//每个区间和乘上d a[L(p)].ff=(a[L(p)].ff*d)%mod;//标记下传 a[R(p)].ff=(a[R(p)].ff*d)%mod;//标记下传 a[L(p)].f=(a[L(p)].f*d)%mod;//综“思路”所述,加法标记也要乘上 a[R(p)].f=(a[R(p)].f*d)%mod;//综“思路”所述,加法标记也要乘上 a[p].ff=1; if(a[p].f)//加法标记的下传 { ll x=a[p].f; a[L(p)].v=((a[L(p)].r-a[L(p)].l+1)*x+a[L(p)].v)%mod; a[R(p)].v=((a[R(p)].r-a[R(p)].l+1)*x+a[R(p)].v)%mod; a[L(p)].f=(a[L(p)].f+x)%mod; a[R(p)].f=(a[R(p)].f+x)%mod; a[p].f=0; } } inline void change(ll p,ll l,ll r,ll x)//区间加 { if(l<=a[p].l&&r>=a[p].r) { a[p].v=((a[p].r-a[p].l+1)*x+a[p].v)%mod; a[p].f=(a[p].f+x)%mod; return; } push_down(p);//标记下传 ll mid=(a[p].l+a[p].r)>>1; if(l<=mid) change(L(p),l,r,x); if(r>mid) change(R(p),l,r,x); doit(p); } inline void change1(ll p,ll l,ll r,ll x)//区间乘法 { if(l<=a[p].l&&r>=a[p].r) { a[p].v=(a[p].v*x)%mod; a[p].ff=(a[p].ff*x)%mod; a[p].f=(a[p].f*x)%mod;//综“思路”所述,加法标记也要乘上 return; } push_down(p); ll mid=(a[p].l+a[p].r)>>1; if(l<=mid) change1(L(p),l,r,x); if(r>mid) change1(R(p),l,r,x); doit(p); } inline ll findout(ll p,ll l,ll r)//区间统计 { if(l<=a[p].l&&r>=a[p].r) return a[p].v; push_down(p); ll mid=(a[p].l+a[p].r)>>1; ll sum=0; if(l<=mid) sum=(sum+findout(L(p),l,r))%mod; if(r>mid) sum=(sum+findout(R(p),l,r))%mod; return sum; } int main() { n=read();m=read(); for(ll i=1;i<=n;i++) aa[i]=read(); build(1,1,n);//冬眠假期刚刚建树,我还有点糊涂 for(ll i=1;i<=m;i++) { char c[5]; scanf("%s",c); if(c[0]==‘Q‘) { ll x=read(),y=read(); ll ans=findout(1,x,y); printf("%lld ",ans); } else if(c[0]==‘+‘) { ll x=read(),y=read(),z=read(); change(1,x,y,z); } else { ll x=read(),y=read(),z=read(); change1(1,x,y,z); } } return 0;//别忘了return 0; }
以上是关于线段树lazy标记:加乘混合的主要内容,如果未能解决你的问题,请参考以下文章
HDU1698 just a Hook - 带有lazy标记的线段树