[AHOI2009]维护序列
题目描述
老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。 有长为N的数列,不妨设为a1,a2,…,aN 。有如下三种操作形式:
(1)把数列中的一段数全部乘一个值;
(2)把数列中的一段数全部加一个值;
(3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值。
输入格式
第一行两个整数N和P(1≤P≤1000000000)。
第二行含有N个非负整数,从左到右依次为a1,a2,…,aN, (0≤ai≤1000000000,1≤i≤N)。
第三行有一个整数M,表示操作总数。
从第四行开始每行描述一个操作,输入的操作有以下三种形式:
操作1:“1 t g c”(不含双引号)。表示把所有满足t≤i≤g的ai改为ai×c(1≤t≤g≤N,0≤c≤1000000000)。
操作2:“2 t g c”(不含双引号)。表示把所有满足t≤i≤g的ai改为ai+c (1≤t≤g≤N,0≤c≤1000000000)。
操作3:“3 t g”(不含双引号)。询问所有满足t≤i≤g的ai的和模P的值 (1≤t≤g≤N)。
同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。
看题面意思就不要多想是一道线段树的题目,但是对一个还弄不懂lazy标记到底是怎么工作的人来说,这道题确实不简单;我刚开始想的是维护每个区间和,然后分开进行加和乘法操作,这是没有错,但是这道题考的就是lazy标记了,加法的lazy就不用多说了吧,关键是乘法,我刚开始想的是加法和乘法分开计算,这样竟然TM过了样例,还全过我想的全部样例,但是交上去就是0分,这让我一度怀疑是输入输出出错了;就这样调了一下午,后来认真看了别人题解,发现这种想法根本就是错的,lazy标记往下传时,可能我加法还没传完,下一个乘法操作就来了,所以我们加法和乘法要一起往下传;那我们明显可以想到乘法优先的传法,即
tree[k<<1].w=(tree[k<<1].w*tree[k].mf+tree[k].af*(tree[k<<1].r-tree[k<<1].l+1))%p;
tree[k<<1|1].w=(tree[k<<1|1].w*tree[k].mf+tree[k].af*(tree[k<<1|1].r-tree[k<<1|1].l+1))%p;
具体操作还是看代码理解,反正我看了很久;
错误代码:
#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL a[200100];
LL n,p;
LL s,x,y,z;
LL sum;
struct Node{
int r,l;
LL w,mf,af;
}tree[800000];
inline void build(int k,int ll,int rr){
tree[k].l=ll,tree[k].r=rr,tree[k].af=0,tree[k].mf=1;
if(ll==rr){
tree[k].w=a[ll];
return;
}
int m=(ll+rr)>>1;
build(k<<1,ll,m);
build(k<<1|1,m+1,rr);
tree[k].w=(tree[k<<1].w+tree[k<<1|1].w)%p;
}
inline void addpd(int k){
if(tree[k].af){
tree[k<<1].af=(tree[k].af+tree[k<<1].af)%p;
tree[k<<1|1].af=(tree[k].af+tree[k<<1|1].af)%p;
tree[k<<1].w=(tree[k<<1].w+tree[k].af*(tree[k<<1].r-tree[k<<1].l+1))%p;
tree[k<<1|1].w=(tree[k<<1|1].w+tree[k].af*(tree[k<<1|1].r-tree[k<<1|1].l+1))%p;
tree[k].af=0;
}
}
inline void mulpd(int k){
if(tree[k].mf>1){
tree[k<<1].mf=(tree[k].mf*tree[k<<1].mf)%p;
tree[k<<1|1].mf=(tree[k].mf*tree[k<<1|1].mf)%p;
tree[k<<1].w=(tree[k<<1].w*tree[k].mf)%p;
tree[k<<1|1].w=(tree[k<<1|1].w*tree[k].mf)%p;
tree[k].mf=1;
}
}
inline void multi(int k){
if(tree[k].l>=x&&tree[k].r<=y){
tree[k].w=(tree[k].w*z)%p;
tree[k].mf=(tree[k].mf*z)%p;
return;
}
mulpd(k),addpd(k);
int m=(tree[k].l+tree[k].r)>>1;
if(x<=m) multi(k<<1);
if(y>m) multi(k<<1|1);
tree[k].w=(tree[k<<1].w+tree[k<<1|1].w)%p;
}
inline void add(int k){
if(tree[k].l>=x&&tree[k].r<=y){
tree[k].w=(tree[k].w+(tree[k].r-tree[k].l+1)*z)%p;
tree[k].af=(tree[k].af+z)%p;
return;
}
mulpd(k),addpd(k);
int m=(tree[k].l+tree[k].r)>>1;
if(x<=m) add(k<<1);
if(y>m) add(k<<1|1);
tree[k].w=(tree[k<<1].w+tree[k<<1|1].w)%p;
}
inline void query(int k){
if(tree[k].l>=x&&tree[k].r<=y){
sum=(sum+tree[k].w)%p;
return;
}
mulpd(k),addpd(k);
int m=(tree[k].l+tree[k].r)>>1;
if(x<=m) query(k<<1);
if(y>m) query(k<<1|1);
}
int main(){
scanf("%lld%lld",&n,&p);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
build(1,1,n);
int m;
scanf("%d",&m);
while(m--){
scanf("%lld%lld%lld",&s,&x,&y);
if(s==1){
scanf("%lld",&z);
multi(1);
}
else if(s==2){
scanf("%lld",&z);
add(1);
}
else if(s==3){
query(1);
printf("%lld\n",sum);
sum=0;
}
}
return 0;
}
正确代码:
#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL a[200100];
LL n,p;
LL s,x,y,z;
LL sum;
struct Node {
int r,l;
LL w,mf,af;
} tree[800000];
inline void build(int k,int ll,int rr){
tree[k].l=ll,tree[k].r=rr,tree[k].af=0,tree[k].mf=1;
if(ll==rr) {
tree[k].w=a[ll];
return;
}
int m=(ll+rr)>>1;
build(k<<1,ll,m);
build(k<<1|1,m+1,rr);
tree[k].w=(tree[k<<1].w+tree[k<<1|1].w)%p;
}
inline void pd(int k){
if(tree[k].af==0&&tree[k].mf==1) return;
tree[k<<1].w=(tree[k<<1].w*tree[k].mf+tree[k].af*(tree[k<<1].r-tree[k<<1].l+1))%p;
tree[k<<1|1].w=(tree[k<<1|1].w*tree[k].mf+tree[k].af*(tree[k<<1|1].r-tree[k<<1|1].l+1))%p;
tree[k<<1].af=(tree[k].af+tree[k<<1].af*tree[k].mf)%p;
tree[k<<1|1].af=(tree[k].af+tree[k<<1|1].af*tree[k].mf)%p;
tree[k<<1].mf=(tree[k].mf*tree[k<<1].mf)%p;
tree[k<<1|1].mf=(tree[k].mf*tree[k<<1|1].mf)%p;
tree[k].af=0,tree[k].mf=1;
}
inline void multi(int k){
if(tree[k].l>=x&&tree[k].r<=y){
tree[k].w=(tree[k].w*z)%p;
tree[k].mf=(tree[k].mf*z)%p;
tree[k].af=(tree[k].af*z)%p;
return;
}
pd(k);
int m=(tree[k].l+tree[k].r)>>1;
if(x<=m) multi(k<<1);
if(y>m) multi(k<<1|1);
tree[k].w=(tree[k<<1].w+tree[k<<1|1].w)%p;
}
inline void add(int k) {
if(tree[k].l>=x&&tree[k].r<=y) {
tree[k].w=(tree[k].w+(tree[k].r-tree[k].l+1)*z)%p;
tree[k].af=(tree[k].af+z)%p;
return;
}
pd(k);
int m=(tree[k].l+tree[k].r)>>1;
if(x<=m) add(k<<1);
if(y>m) add(k<<1|1);
tree[k].w=(tree[k<<1].w+tree[k<<1|1].w)%p;
}
inline void query(int k) {
if(tree[k].l>=x&&tree[k].r<=y) {
sum=(sum+tree[k].w)%p;
return;
}
pd(k);
int m=(tree[k].l+tree[k].r)>>1;
if(x<=m) query(k<<1);
if(y>m) query(k<<1|1);
}
int main() {
scanf("%lld%lld",&n,&p);
for(int i=1; i<=n; i++) scanf("%lld",&a[i]);
build(1,1,n);
int m;
scanf("%d",&m);
while(m--) {
scanf("%lld%lld%lld",&s,&x,&y);
if(s==1) {
scanf("%lld",&z);
multi(1);
} else if(s==2) {
scanf("%lld",&z);
add(1);
} else if(s==3) {
query(1);
printf("%lld\n",sum);
sum=0;
}
}
return 0;
}