线段树2
Posted popo-black-cat
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树2相关的知识,希望对你有一定的参考价值。
本以为自己对线段树已经掌握得很好,做完这道题,感觉自己又收货了很多。
首先我们要定义运算的优先顺序,因为对于两个lazy标记,我们不知道是应该先乘还是应该先加,所以我们就规定:先乘再加,就这么定了!
可是别高兴太早呀!我们虽然规定了顺序,但是这不一定是真正的顺序,比如 ( a + b ) * c 和 a * c + b 是不一样的,所以呐,我们略加观察——嘿!原来在原有基础上乘一个数,和先乘这个数,与加上加法lazy乘这个 数的和是一样的!那么,我们就可以这样:如果加一个数,就直接加在加法lazy上;如果是乘一个数,那么我们除了在乘法lazy里乘上这个数以外,也在加法lazy里乘这个数,这样先乘后加就一定不会有错啦!当然前提是乘法标记一开始都是1!
还有一件事,就是lazy标记的下传。比如儿子节点是 ac + b ,父亲节点乘法标记和加法标记是 x , y 。那这个值应该是 ( ac + b ) * x + y,展开后就是 acx + bx + y,那我们发现,这就是在儿子节点的初始权值和的基础上,乘法标记是 c * x ,加法标记是 bx + y。所以我们可以乘法直接乘,加法先乘上父亲节点的乘法标记,再加上父亲节点的加法标记,就是下传后的标记了。
那话不多说啦(已经说很多了),代码如下:
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; #define maxn 2000010 #define int long long struct node { int sum,add,mul; } e[maxn]; int l[maxn],r[maxn]; int n,m,p; int add(int a,int b) { return (a+b)%p; } int mul(int a,int b) { return (a*b)%p; } void push_down(int now) { int ls=now*2; int rs=now*2+1; int mid=(l[now]+r[now])/2; e[ls].sum=mul(e[ls].sum,e[now].mul); e[ls].sum=add(e[ls].sum,e[now].add*(r[ls]-l[ls]+1)); e[rs].sum=mul(e[rs].sum,e[now].mul); e[rs].sum=add(e[rs].sum,e[now].add*(r[rs]-l[rs]+1)); e[ls].mul=mul(e[ls].mul,e[now].mul); e[rs].mul=mul(e[rs].mul,e[now].mul); e[ls].add=mul(e[ls].add,e[now].mul); e[ls].add=add(e[ls].add,e[now].add); e[rs].add=mul(e[rs].add,e[now].mul); e[rs].add=add(e[rs].add,e[now].add); e[now].mul=1; e[now].add=0; } void build(int L,int R,int now) { e[now].mul=1; e[now].add=0; l[now]=L; r[now]=R; if(L==R) { scanf("%lld",&e[now].sum); e[now].sum%=p; return; } int mid=(L+R)/2; build(L,mid,now*2); build(mid+1,R,now*2+1); e[now].sum=add(e[now*2].sum,e[now*2+1].sum); return ; } void update_add(int now,int L,int R,int k) { if(L<=l[now]&&r[now]<=R) { e[now].add=add(e[now].add,k); e[now].sum=add(e[now].sum,k*(r[now]-l[now]+1)); return ; } push_down(now); int mid=(l[now]+r[now])/2; if(mid>=L) update_add(now*2,L,R,k); if(mid<R) update_add(now*2+1,L,R,k); e[now].sum=add(e[now*2].sum,e[now*2+1].sum); } void update_mul(int now,int L,int R,int k) { if(L<=l[now]&&r[now]<=R) { e[now].mul=mul(e[now].mul,k); e[now].add=mul(e[now].add,k); e[now].sum=mul(e[now].sum,k); return ; } push_down(now); int mid=(l[now]+r[now])/2; if(mid>=L) update_mul(now*2,L,R,k); if(mid<R) update_mul(now*2+1,L,R,k); e[now].sum=add(e[now*2].sum,e[now*2+1].sum); } int query(int now,int L,int R) { if(L<=l[now]&&r[now]<=R) return e[now].sum; push_down(now); int mid=(l[now]+r[now])/2; int ans=0; if(L<=mid) ans=query(now*2,L,R)%p; if(mid<R) ans=add(ans,query(now*2+1,L,R)); return ans; } main() { scanf("%lld%lld%lld",&n,&m,&p); build(1,n,1); for(int i=1;i<=m;i++) { int op,x,y,k; scanf("%lld%lld%lld",&op,&x,&y); if(op==1) { scanf("%lld",&k); update_mul(1,x,y,k); } else if(op==2) { scanf("%lld",&k); update_add(1,x,y,k); } else printf("%lld ",query(1,x,y)); } return 0; }
啊啊啊,还有一道序列操作没 d 出来……
所以——
睡觉去了!
>\<
以上是关于线段树2的主要内容,如果未能解决你的问题,请参考以下文章