题目传送门
这题的正解是线段树,这个其实还是比较好想的,因为这道题中的所有操作都是线段树的基本操作。
但是,这题的延迟标记有两个,这两个标记之间的关系是比较难想的,而且也是比较容易打错的……
这道题主要考察的是选手对于线段树的延迟标记的理解程度,整个程序最难打的也就是pushdown这个函数,我也在这个函数上WA了好几次。(这仅代表个人意见,如有同学认为这个函数好打,那你厉害了)
附上AC代码:
#include <cstdio>
#define lt (k<<1)
#define rt (k<<1|1)
#define mid (l+r>>1)
using namespace std;
struct note{
long long jia,cheng,w;
}t[300010];
long long n,m,p,x,y,o,c,cheng,jia;
void build(long long k,long long l,long long r){
t[k].jia=0,t[k].cheng=1;
if (l==r){
scanf("%lld",&t[k].w),t[k].w%=p;
return;
}
build(lt,l,mid);
build(rt,mid+1,r);
t[k].w=(t[lt].w+t[rt].w)%p;
return;
}
void push(long long k,long long l,long long r){
t[lt].w=(t[lt].w*t[k].cheng+(mid-l+1)*t[k].jia)%p;
t[lt].cheng=t[lt].cheng*t[k].cheng%p;
t[lt].jia=(t[lt].jia*t[k].cheng+t[k].jia)%p;
t[rt].w=(t[rt].w*t[k].cheng+(r-mid)*t[k].jia)%p;
t[rt].cheng=t[rt].cheng*t[k].cheng%p;
t[rt].jia=(t[rt].jia*t[k].cheng+t[k].jia)%p;
t[k].cheng=1,t[k].jia=0;return;
}
void change(long long k,long long l,long long r,long long ql,long long qr){
if (l>qr||r<ql) return;
if (l>=ql&&r<=qr){
t[k].w=(t[k].w*cheng+(r-l+1)*jia)%p;
t[k].cheng=t[k].cheng*cheng%p;
t[k].jia=(t[k].jia*cheng+jia)%p;
return;
}
push(k,l,r);
if (ql<=mid) change(lt,l,mid,ql,qr);
if (qr>mid) change(rt,mid+1,r,ql,qr);
t[k].w=(t[lt].w+t[rt].w)%p;
return;
}
long long query(long long k,long long l,long long r,long long ql,long long qr){
if (l>qr||r<ql) return 0;
if (l>=ql&&r<=qr) return t[k].w%p;
push(k,l,r);
long long ans=0;
if (ql<=mid) ans=(ans+query(lt,l,mid,ql,qr))%p;
if (qr>mid) ans=(ans+query(rt,mid+1,r,ql,qr))%p;
return ans%p;
}
int main(void){
scanf("%lld%lld",&n,&p);
build(1,1,n);
scanf("%lld",&m);
for (long long i=1; i<=m; ++i){
scanf("%lld%lld%lld",&o,&x,&y);
switch (o){
case 1:scanf("%lld",&cheng),jia=0,change(1,1,n,x,y);break;
case 2:scanf("%lld",&jia),cheng=1,change(1,1,n,x,y);break;
case 3:printf("%lld\n",query(1,1,n,x,y));break;
}
}
return 0;
}