题目
https://www.luogu.org/problemnew/show/3373
就是维护一个序列,有区间加或乘的操作,区间查询。
分析
- 线段树弄两种 tag,乘和加,推一下tag操作时的公式即可。
- 具体可以看程序中的注释。
程序
#include <cstdio>
typedef long long ll;
ll n,m,Ha,k,k1,k2,k3;
struct segment_tree{
#define X t[x]
#define Lx (x<<1)
#define Rx (Lx+1)
#define L t[Lx]
#define R t[Rx]
#define Mid ((X.l+X.r)>>1)
#define sz(x) (t[x].r-t[x].l+1)
struct node{ll s,l,r,T1,T2;} t[2000000]; //[l,r]: 区间*t1+t2
void mer(ll x){
X.s=(L.s*L.T1+sz(Lx)*L.T2)%Ha;
X.s=(X.s+R.s*R.T1+sz(Rx)*R.T2)%Ha;
}
void bui(ll x,ll l,ll r){
X=(node){
0,l,r,1,0};
if (l==r) {scanf("%d",&X.s); return;}
bui(Lx,l,Mid);
bui(Rx,Mid+1,r);
mer(x);
}
void add_tag(ll x,ll t1,ll t2){ //(x*T1+T2)*t1+t2=x*(T1*t1)+T2*t1+t2
X.T1=(X.T1*t1)%Ha;
X.T2=(X.T2*t1+t2)%Ha;
}
void down_tag(ll x){ //sigma(i*T1+T2)=sigma(i)*T1+sz*T2
add_tag(Lx,X.T1,X.T2);
add_tag(Rx,X.T1,X.T2);
ll sz=X.r-X.l+1;
X.s=(X.s*X.T1+sz*X.T2)%Ha;
X.T1=1,X.T2=0;
}
ll que(ll x,ll l,ll r){ //询问 [l,r]
down_tag(x);
if (l<=X.l && X.r<=r) return X.s;
ll ret=0;
if (l<=Mid) ret=(ret+que(Lx,l,r))%Ha;
if (r>Mid) ret=(ret+que(Rx,l,r))%Ha;
return ret;
}
void upd(ll x,ll l,ll r,ll t1,ll t2){ //[l,r]*t1+t2
if (l<=X.l && X.r<=r){add_tag(x,t1,t2); return;}
down_tag(x);
if (l<=Mid) upd(Lx,l,r,t1,t2);
if (r>Mid) upd(Rx,l,r,t1,t2);
mer(x);
}
} T;
int main(){
scanf("%d%d%d",&n,&m,&Ha);
T.bui(1,1,n);
while (m--){
scanf("%d",&k);
if (k==1){
scanf("%d%d%d",&k1,&k2,&k3);
T.upd(1,k1,k2,k3,0);
continue;
}
if (k==2){
scanf("%d%d%d",&k1,&k2,&k3);
T.upd(1,k1,k2,1,k3);
continue;
}
scanf("%d%d",&k1,&k2);
printf("%d\n",T.que(1,k1,k2));
}
}