@[_buzhidao_](/user/917775) 你的spread对tag的下放好像有问题,如果遇到乘法操作,那么区间内的加法标记也是要乘的
好比同一个区间加上五再乘以三,那么加法标记应修改为十五,还要注意乘法和加法的操作顺序。。
by HeYilin @ 2024-04-10 07:06:58
@[HeYilin](/user/728459) 这么说 `upd2` 也有类似的问题。。。
by _buzhidao_ @ 2024-04-10 19:45:58
@[HeYilin](/user/728459) 改成了这个样子:
```cpp
if(t[p].tag2){
tmp=t[p].tag2;
t[p*2].va=(t[p*2].va*tmp)%m;
t[p*2+1].va=(t[p*2+1].va*tmp)%m;
t[p*2].tag2=(t[p*2].tag2*tmp)%m;t[p*2+1].tag2=(t[p*2+1].tag2*tmp)%m;
t[p*2].tag1=(t[p*2].tag1*tmp)%m;t[p*2+1].tag1=(t[p*2+1].tag1*tmp)%m;//here
t[p].tag2=0;
}
```
```cpp
void upd2(int p,int l,int r,int k){//bug
if(t[p].l>=l&&t[p].r<=r){
t[p].va=(t[p].va*k)%m;
t[p].tag2=(t[p].tag2*k)%m;
t[p].tag1=(t[p].tag1*k)%m;//here
return;
}
spread(p);
int mid=(t[p].l+t[p].r)>>1;
if(l<=mid) upd2(p*2,l,r,k);
if(r>mid) upd2(p*2+1,l,r,k);
t[p].va=(t[p*2].va+t[p*2+1].va)%m;
}
```
自测#1不通过,继续求助orz
by _buzhidao_ @ 2024-04-10 19:49:55
@[_buzhidao_](/user/917775) 我有点忍不住想爆粗口。。这么说吧,如果你的tag2是乘法标记,那么你应该把不需要处理的状态标记成1而不是0,因为虽然你这样标记处理好像也没问题(?,但是如果数据给你的全是乘1那么你的代码执行效率会大大降低;其次如果标成1的话,需要在建树的时候标记;处理懒标记,下放时要先干数值,再干乘法,最后做加法标记,这样加法标记不会漏乘:想一想,如果乘法的懒标记还没有下放,就用没下放的乘法标记修改加法,那么父节点应该下放的那个数就少乘了,自然就不对
by HeYilin @ 2024-04-10 22:19:04
我这里放一下我的代码,里面乘法对应tag1,加法对应tag2,你可以对照看一下,注意一下初始化和标记,我现在不太方便调试你的代码(qwq
```cpp
#include<bits/stdc++.h>
typedef long long int ll;
using namespace std;
const int maxn=1e5+10;
ll n,mod,q,a[maxn],op,x,y,k;
struct SegmentTree{
ll l,r;
ll sum;
ll tag1=1,tag2;
}t[maxn<<2];
void build(ll p,ll l,ll r){
t[p].l=l,t[p].r=r;
if(l==r){t[p].sum=a[l]%mod;return;}
ll mid=l+r>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
t[p].sum=(t[p*2].sum+t[p*2+1].sum)%mod;
}
void pushdown(ll p){
//sum
t[p*2].sum*=t[p].tag1;t[p*2].sum%=mod;
t[p*2].sum+=t[p].tag2*(t[p*2].r-t[p*2].l+1);t[p*2].sum%=mod;
t[p*2+1].sum*=t[p].tag1;t[p*2+1].sum%=mod;
t[p*2+1].sum+=t[p].tag2*(t[p*2+1].r-t[p*2+1].l+1);t[p*2+1].sum%=mod;
//tag1
t[p*2].tag1*=t[p].tag1;t[p*2].tag1%=mod;
t[p*2+1].tag1*=t[p].tag1;t[p*2+1].tag1%=mod;
//tag2
t[p*2].tag2*=t[p].tag1;t[p*2].tag2%=mod;
t[p*2].tag2+=t[p].tag2;t[p*2].tag2%=mod;
t[p*2+1].tag2*=t[p].tag1;t[p*2+1].tag2%=mod;
t[p*2+1].tag2+=t[p].tag2;t[p*2+1].tag2%=mod;
//t[p]
t[p].tag1=1,t[p].tag2=0;
}
void change1(ll p,ll l,ll r,ll d){// * x
if(l<=t[p].l&&r>=t[p].r){
t[p].sum*=d;t[p].sum%=mod;
t[p].tag1*=d;t[p].tag1%=mod;
t[p].tag2*=d;t[p].tag2%=mod;//
return;
}
pushdown(p);
ll mid=t[p].l+t[p].r>>1;
if(l<=mid)change1(p*2,l,r,d);
if(r>mid)change1(p*2+1,l,r,d);
t[p].sum=(t[p*2].sum+t[p*2+1].sum)%mod;
}
void change2(ll p,ll l,ll r,ll d){// + x
if(l<=t[p].l&&r>=t[p].r){
t[p].sum+=d*(t[p].r-t[p].l+1);t[p].sum%=mod;
t[p].tag2+=d;t[p].tag2%=mod;
return;
}
pushdown(p);
ll mid=t[p].l+t[p].r>>1;
if(l<=mid)change2(p*2,l,r,d);
if(r>mid)change2(p*2+1,l,r,d);
t[p].sum=(t[p*2].sum+t[p*2+1].sum)%mod;
}
ll query(ll p,ll l,ll r){
if(l<=t[p].l&&r>=t[p].r)return t[p].sum;
pushdown(p);
ll mid=t[p].l+t[p].r>>1;
ll val=0;
if(l<=mid)val+=query(p*2,l,r);
if(r>mid)val+=query(p*2+1,l,r);
return val%mod;
}
int main(){
cin>>n>>q>>mod;
for(int i=1;i<=n;i++)cin>>a[i];
build(1,1,n);
while(q--){
cin>>op>>x>>y;
if(op==1){
cin>>k;
change1(1,x,y,k);
}
if(op==2){
cin>>k;
change2(1,x,y,k);
}
if(op==3)cout<<query(1,x,y)<<"\n";
}
return 0;
}
/*
5 5 38
1 5 4 2 3
2 1 4 1
3 2 5
1 2 4 2
2 3 5 5
3 1 4
*/
```
by HeYilin @ 2024-04-10 22:22:26
@[HeYilin](/user/728459) 没错,我判断 `tag2==0` 其实是个bug,您的指导我明天再精读orz%%%
by _buzhidao_ @ 2024-04-10 22:46:04
@[HeYilin](/user/728459) 改成了这个样子
```cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
//#define read(x) cin>>x
template<typename T>inline void read(T &x){
x=0;int f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)) x=(x<<1)+(x<<3)+(c^48),c=getchar();
x*=f;
}
int n,q,m,a,b,c,d;ll s[(int)4e5];
struct tree{
int l,r;ll va,tag1,tag2;
} t[(int)4e5+5];
void buildt(int p,int l,int r){
t[p].l=l,t[p].r=r;t[p].tag2=1;
if(l==r){
t[p].va=s[l]%m;return;
}
int mid=(l+r)>>1;
buildt(p*2,l,mid);
buildt(p*2+1,mid+1,r);
t[p].va=(t[p*2].va+t[p*2+1].va)%m;
}
void spread(int p){//
ll tmp1=t[p].tag1,tmp2=t[p].tag2;
//va
t[p*2].va=(t[p*2].va*tmp2)%m;
t[p*2+1].va=(t[p*2+1].va*tmp2)%m;
t[p*2].va=(t[p*2].va+tmp1*(t[p*2].r-t[p*2].l+1))%m;
t[p*2+1].va=(t[p*2+1].va+tmp1*(t[p*2+1].r-t[p*2+1].l+1))%m;
//tag2
t[p*2].tag2=(t[p*2].tag2*tmp2)%m;t[p*2+1].tag2=(t[p*2+1].tag2*tmp2)%m;
//tag1
t[p*2].tag1=(t[p*2].tag1+tmp1)%m;t[p*2+1].tag1=(t[p*2+1].tag1+tmp1)%m;
t[p*2].tag1=(t[p*2].tag1*tmp2)%m;t[p*2+1].tag1=(t[p*2+1].tag1*tmp2)%m;
//t[p]
t[p].tag1=0;t[p].tag2=1;
}
void upd1(int p,int l,int r,int k){
if(t[p].l>=l&&t[p].r<=r){
t[p].va=(t[p].va+(ll)k*(t[p].r-t[p].l+1))%m;
t[p].tag1=(t[p].tag1+k)%m;
return;
}
spread(p);
int mid=(t[p].l+t[p].r)>>1;
if(l<=mid) upd1(p*2,l,r,k);
if(r>mid) upd1(p*2+1,l,r,k);
t[p].va=(t[p*2].va+t[p*2+1].va)%m;
}
void upd2(int p,int l,int r,int k){
if(t[p].l>=l&&t[p].r<=r){
t[p].va=(t[p].va*k)%m;
t[p].tag2=(t[p].tag2*k)%m;
t[p].tag1=(t[p].tag1*k)%m;
return;
}
spread(p);
int mid=(t[p].l+t[p].r)>>1;
if(l<=mid) upd2(p*2,l,r,k);
if(r>mid) upd2(p*2+1,l,r,k);
t[p].va=(t[p*2].va+t[p*2+1].va)%m;
}
ll query(int p,int l,int r){
if(l<=t[p].l&&r>=t[p].r){/*cout<<t[p].l<<t[p].r<<' ';*/return t[p].va;}
spread(p);
int mid=(t[p].l+t[p].r)>>1;ll ans=0;
if(l<=mid) ans+=query(p*2,l,r);
if(r>mid) ans+=query(p*2+1,l,r);
return ans%m;
}
int main(){
read(n);read(q);read(m);
for(int i=1;i<=n;++i) read(s[i]);
buildt(1,1,n);
for(int i=0;i<q;++i){
read(a);
if(a==1){
read(b);read(c);read(d);
upd2(1,b,c,d%m);
}
else if(a==2){
read(b);read(c);read(d);
upd1(1,b,c,d%m);
}
else if(a==3){
read(b);read(c);
cout<<query(1,b,c)<<endl;
}
//if(i==1) for(int i=1;i<=4*n;++i) if(t[i].va!=0) cout<<t[i].va<<' '<<t[i].l<<' '<<t[i].r<<endl;
/*for(int i=1;i<=n;++i){
cout<<query(1,i,i)<<' ';
}cout<<endl;*/
}
return 0;
}
```
by _buzhidao_ @ 2024-04-12 06:49:34
@[HeYilin](/user/728459) 0,但是每个点前一两行能过
by _buzhidao_ @ 2024-04-12 06:59:15
@[_buzhidao_](/user/917775) 下放加法的问题。可以看一下我的代码。如果是保证每次修改都将加法操作的标记乘上乘法标记,那么下放时当前节点的加法标记应该是先先乘后加,因为加法的标记已经先处理过了,乘完乘法标记了,所以先加在乘的话,加法标记的数值是被乘了两遍的,具体的好像改一下顺序就可以。
by HeYilin @ 2024-04-12 07:06:37
@[_buzhidao_](/user/917775) 也就是你spread中tag1的那两行
by HeYilin @ 2024-04-12 07:09:26