线段树2对于Pushdown的理解
关于“线段树2”~3373
最近才把这玩意儿搞出来,代码和解说如下:
#include<cstdio>
#include<cstring>
using namespace std;
const int N=100005;
struct sd{
int son[2],l,r;
long long sum,add,pass;
sd()
{
memset(this,0,sizeof(this));
pass=1;
}
}node[N*2];
int ini[N],root,cnt=0,q;
void Buildtree(int &k,int l,int r)
{
k=++cnt;
node[k].l=l;
node[k].r=r;
if(l==r)
{
node[k].sum=ini[l];
}
else
{
int mid=(l+r)/2;
Buildtree(node[k].son[0],l,mid);
Buildtree(node[k].son[1],mid+1,r);
node[k].sum=node[node[k].son[0]].sum+node[node[k].son[1]].sum;
}
}
void pushdown(int k)
{
sd L,R,Main;
L=node[node[k].son[0]];
R=node[node[k].son[1]];
Main=node[k];
L.sum=(L.sum*Main.pass+Main.add*(L.r-L.l+1))%q;
R.sum=(R.sum*Main.pass+Main.add*(R.r-R.l+1))%q;
L.pass=(L.pass*Main.pass)%q;
R.pass=(R.pass*Main.pass)%q;
L.add=(L.add*Main.pass+Main.add)%q;
R.add=(R.add*Main.pass+Main.add)%q;
Main.add=0; Main.pass=1;
node[k]=Main;//☣
node[node[k].son[0]]=L;
node[node[k].son[1]]=R;
return;
}
void modify1(int k,int ql,int qr,int val)
{
if(node[k].l==ql&&node[k].r==qr)
{
node[k].add=(node[k].add+val)%q;
node[k].sum=(node[k].sum+val*(node[k].r-node[k].l+1))%q;
}
else
{
pushdown(k);
int mid=(node[k].l+node[k].r)/2;
if(ql>mid) modify1(node[k].son[1],ql,qr,val);
else if(qr<=mid) modify1(node[k].son[0],ql,qr,val);
else
{
modify1(node[k].son[0],ql,mid,val);
modify1(node[k].son[1],mid+1,qr,val);
}
node[k].sum=node[node[k].son[0]].sum+node[node[k].son[1]].sum;
}
}
void modify2(int k,int ql,int qr,int val)
{
if(node[k].l==ql&&node[k].r==qr)
{
//node[k].add=(node[k].add*val)%q;//一个存疑点
node[k].pass=(node[k].pass*val)%q;
node[k].sum=(node[k].sum*val)%q;
}
else
{
pushdown(k);
int mid=(node[k].l+node[k].r)/2;
if(ql>mid) modify2(node[k].son[1],ql,qr,val);
else if(qr<=mid) modify2(node[k].son[0],ql,qr,val);
else
{
modify2(node[k].son[0],ql,mid,val);
modify2(node[k].son[1],mid+1,qr,val);
}
node[k].sum=node[node[k].son[0]].sum+node[node[k].son[1]].sum;
}
}
long long query(int k,int l,int r,int n)
{
if(node[k].l==l&&node[k].r==r)
{
pushdown(k);
return node[k].sum;
}
else
{
pushdown(k);
int mid=(node[k].l+node[k].r)/2;
if(mid>=r) return query(node[k].son[0],l,r,n);
else if(mid<l) return query(node[k].son[1],l,r,n);
else
{
return query(node[k].son[0],l,mid,n)+query(node[k].son[1],mid+1,r,n);
}
}
}
int main()
{
int m,n;
scanf("%d%d%d",&m,&n,&q);
int a,b,c,d;
for(int i=1;i<=m;++i)
{
scanf("%d",&ini[i]);
}
Buildtree(root,1,m+5);
for(int i=1;i<=n;++i)
{
scanf("%d",&d);
if(d==1)
{
scanf("%d%d%d",&a,&b,&c);
modify2(root,a,b,c);
}
else if(d==2)
{
scanf("%d%d%d",&a,&b,&c);
modify1(root,a,b,c);
}
else
{
scanf("%d%d",&a,&b);
printf("%lld\n",query(root,a,b,q)%q);
}
}
return 0;
}
其中Pushdown是本题的精髓
正常的两种Pushdown写法:
方法一:
void pushdown(int k)
{
sd L,R,Main;
L=node[node[k].son[0]];
R=node[node[k].son[1]];
Main=node[k];
L.sum=(L.sum*Main.pass+Main.add*(L.r-L.l+1))%q;
R.sum=(R.sum*Main.pass+Main.add*(R.r-R.l+1))%q;
L.pass=(L.pass*Main.pass)%q;
R.pass=(R.pass*Main.pass)%q;
L.add=(L.add*Main.pass+Main.add)%q;
R.add=(R.add*Main.pass+Main.add)%q;
Main.add=0; Main.pass=1;
node[k]=Main;
node[node[k].son[0]]=L;
node[node[k].son[1]]=R;
return;
}
方法二:
void Pushdown(int k)
{
node[node[k].son[0]].sum=(node[node[k].son[0]].sum*node[k].pass + node[k].add* ( node[node[k].son[0]].r - node[node[k].son[0]].l+1) ) %q;
node[node[k].son[1]].sum=(node[node[k].son[1]].sum*node[k].pass + node[k].add*( node[node[k].son[1]].r - node[node[k].son[1]].l+1) ) %q;
node[node[k].son[0]].pass=(node[node[k].son[0]].pass*node[k].pass)%q;
node[node[k].son[1]].pass=(node[node[k].son[1]].pass*node[k].pass)%q;
node[node[k].son[0]].add=(node[node[k].son[0]].add*node[k].pass+node[k].add)%q;
node[node[k].son[1]].add=(node[node[k].son[1]].add*node[k].pass+node[k].add)%q;
node[k].add=0; node[k].pass=1;
return 0;
}
WA掉的方法:
void Pushdown(int k)
{
node[node[k].son[0]].add+=node[k].add;//写错了
node[node[k].son[1]].add+=node[k].add;//写错了
node[node[k].son[0]].pass*=node[k].pass;
node[node[k].son[1]].pass*=node[k].pass;
node[node[k].son[0]].sum=node[node[k].son[0]].sum*node[node[k].son[0]].pass+(node[node[k].son[0]].r-node[node[k].son[0]].l+1);
node[node[k].son[1]].sum=node[node[k].son[1]].sum*node[node[k].son[1]].pass+(node[node[k].son[1]].r-node[node[k].son[1]].l+1);
node[k].add=0; node[k].pass=1;
return 0;
}
如何理解此Pushdown呢(大家可以对照那份WA掉的看)?
首先前两行是来更新父节点下左右儿子的和一定要遵循*先乘后加*的原则,并且在加的时候,一定不忘乘上区间的长度这样更新才不会错!!后面四步是基于要“薪火相传”的原则,以后Pushdown的时候可以Pushdown给他们自己的儿子。
所以中间两步是先来传乘法(pass)直接用儿子的乘(L&R.pass)乘上父亲转下来的乘(Main.pass)。
后面两步则是传下父亲的加,但是要注意,传加的时候应该先用儿子原有的加乘上父亲的乘(因为我们遵循“*先乘后加*”的原则,在第一阶段,原有的加应该会被父亲的乘成倍扩大(和区间和(sum)一起)!!!)
关于modify1,modify2中的改变
本人写程序的时候,可能脑袋有点抽,modify1和操作1不是对应的,modify1对应操作2~~~~~~
先来看一下和乘有关的modify
void modify2(int k,int ql,int qr,int val)
{
if(node[k].l==ql&&node[k].r==qr)
{
node[k].add=(node[k].add*val)%q;
node[k].pass=(node[k].pass*val)%q;
node[k].sum=(node[k].sum*val)%q;
}
//后面还有省略,这里只是相对于线段树1有稍微改变的地方
乘法modify主要要注意的问题,还是要注意要更新加(add)中的东西(我想了一下,介于有Pushdown我也觉得没有那个必要)然后就是正常更新儿子中乘(pass)和总和(sum)。
再来看一下和加有关的modify
} void modify1(int k,int ql,int qr,int val)
{
if(node[k].l==ql&&node[k].r==qr)
{
node[k].add=(node[k].add+val)%q;
node[k].sum=(node[k].sum+val*(node[k].r-node[k].l+1))%q;
}
//后面还有省略,这里只是相对于线段树1有稍微改变的地方
正常修改儿子中的加(add),但是最后一定不要忘记修改儿子总和时(sum)(因为有Pushdown,所以我们不用考虑“先乘后加”原则),只要记住乘上区间长度即可。
最后(几点提醒)
1、Pushdown中一定不要忘了把建立的临时变量里的东西传回node中!!!血的教训啊!耗了我一个多小时!!!*
2、modify中Pushdown已经帮你做了很多事情了,所以不用think to much!
3、更多警告可以看一下我的线段树Coding Caution
P.S:有人可能会问我,为什么在Pushdown中更新下面的总值的时候为什么不把要更新的点的Pass也乘进去,是因为在modify的时候我已经用他原来的Pass值更新过一遍了,当然不需要乘进去,那又会有人问,为什么要把pass的值和add的值又更新到要更新的点的Pass和add上呢?这个则是因为后面又要Pushdown的时候,一定要连着这个一起Pushdown下去。
2018.10.16
下面放一个写的比较清晰的线段树2:
AC code:
#include<bits/stdc++.h>
#define N 100004
#define LL long long
using namespace std;
struct sd{
LL son[2],l,r,pass,add,sum;
sd(){pass=1;}
}node[N*10];
LL p,ini[N],root,cnt,n,m;
void update(LL k){node[k].sum=node[node[k].son[0]].sum+node[node[k].son[1]].sum;}
void add_pass(LL k,LL val)
{
node[k].pass=(node[k].pass*val)%p;node[k].sum=(node[k].sum*val)%p;
node[k].add=(node[k].add*val)%p;
}
void add_add(LL k,LL val)
{
node[k].sum=(val*(node[k].r-node[k].l+1)+node[k].sum)%p;
node[k].add=(node[k].add+val)%p;
}
void pushdown(LL k)
{
add_pass(node[k].son[0],node[k].pass); add_pass(node[k].son[1],node[k].pass);node[k].pass=1;
add_add(node[k].son[0],node[k].add); add_add(node[k].son[1],node[k].add);node[k].add=0;
}
void Buildtree(LL &k,LL l,LL r)
{
k=++cnt;node[k].l=l;node[k].r=r;
if(l==r) node[k].sum=ini[l];
else {LL mid=(l+r)/2;Buildtree(node[k].son[0],l,mid);Buildtree(node[k].son[1],mid+1,r);update(k);}
}
void modify_pass(LL k,LL l,LL r,LL val)
{
if(node[k].l==l&&node[k].r==r) add_pass(k,val);
else
{
pushdown(k); LL mid=(node[k].l+node[k].r)/2;
if(r<=mid) modify_pass(node[k].son[0],l,r,val);
else if(l>mid) modify_pass(node[k].son[1],l,r,val);
else modify_pass(node[k].son[0],l,mid,val),modify_pass(node[k].son[1],mid+1,r,val);
update(k);
}
}
void modify_add(LL k,LL l,LL r,LL val)
{
if(node[k].l==l&&node[k].r==r) add_add(k,val);
else
{
pushdown(k); LL mid=(node[k].l+node[k].r)/2;
if(r<=mid) modify_add(node[k].son[0],l,r,val);
else if(l>mid) modify_add(node[k].son[1],l,r,val);
else modify_add(node[k].son[0],l,mid,val),modify_add(node[k].son[1],mid+1,r,val);
update(k);
}
}
LL query(LL k,LL l,LL r)
{
if(node[k].l==l&&node[k].r==r) return node[k].sum%p;
else
{
pushdown(k); LL mid=(node[k].l+node[k].r)/2;
if(r<=mid) return query(node[k].son[0],l,r)%p;
else if(l>mid) return query(node[k].son[1],l,r)%p;
else return (query(node[k].son[0],l,mid)+query(node[k].son[1],mid+1,r))%p;
}
}
int main()
{
scanf("%d%d%d",&n,&m,&p);
for(int i=1;i<=n;++i) scanf("%d",&ini[i]);
Buildtree(root,1,n);int a,b,c,d;
for(int i=1;i<=m;++i)
{
scanf("%d",&a);
if(a==1) scanf("%d%d%d",&b,&c,&d),modify_pass(root,b,c,d);
if(a==2) scanf("%d%d%d",&b,&c,&d),modify_add(root,b,c,d);
if(a==3) scanf("%d%d",&b,&c),printf("%d\n",query(root,b,c));
}
return 0;
}
指针版的线段树戳这里☞指针版的线段树