题解 P3373 【【模板】线段树 2】
我这个解法的不同之处在于通过指针来访问线段树上的其他节点,感觉这样逻辑会更清晰一点。
还有觉得楼下milkfilling说的不对,考虑加法优先的话应该是tag2(他的add)会出现精度问题,但tag1(他的mul)不会,他正好说反了。
1.我们在【模板1】的基础上考虑在区间上乘x 2.直接想到加一个tag2来表示乘多少 3.考虑你每个node有d,tag1,tag2这三个属性,如何维护该区间的值。
a)先乘再加:想到区间和=dtag1+tag2len(那么我们应该怎样维护tag1和tag2保证其正确性呢),一个非常朴素的想法是每次【乘法修改】tag1=x。但这样是不对的,因为此时你该区间和上乘个x,即区间和=x,应当是(dtag1+tag2len)x,所以修改应该是tag1=x和tag2*=x;对于每次【加法修改】,我们应当不改变tag1,tag2+=x。
b)先加再乘:还有另一种维护区间和的方式即,区间和=(d+tag2len)tag1,这个时候我们思考应该怎样修改tag1,tag2保证其答案正确。对于每次【乘法修改】,tag1=x,tag2不变;对于每次【加法修改】(d+tag2len)tag1+xlen=(d+len(x/tag1+tag2))tag1,即tag2+=x/tag1,tag1不变。
以上是这道题的核心部分,附上代码:
#include<iostream>
#define MAXN 100000
#define LL long long
using namespace std;
int top;
struct node{
int l,r;
LL d,tag1,tag2;//tag1乘,tag2加
node *ls,*rs;
}pool[4*MAXN];
LL a[MAXN+5],N,M,P;
node* buildT(int l,int r){
node* p=pool+(++top);
if(l==r){
p->l=p->r=l;
p->d=a[l]%P;
p->tag1=1;
return p;
}
p->tag1=1;
p->l=l; p->r=r;
p->ls=buildT(l,(l+r)/2); p->rs=buildT((r+l)/2+1,r);
p->d=(p->ls->d+p->rs->d)%P;
return p;
}
LL getData(node *p){
return (p->d*p->tag1%P+p->tag2*(p->r-p->l+1)%P)%P;
}
//pushdown的本质是根据运算的优先级,维护算法正确性
//区域加法在query时pushdown可以因为+3 +5 先加3还是先加5对答案没有区别;但区域乘法在update时就要pushdown因为+3 *5 先后顺序有区别
void pushdown(node *p){
if(p->tag1==1 && p->tag2==0) return;
p->d=getData(p);
//如果这个时候rs或ls的tag上有值,那么说明这些操作是在p上有操作之前的(因为之前一直有push down)。所以他们享有被tag1乘的权利
p->rs->tag1=p->rs->tag1*p->tag1%P;
p->rs->tag2=(p->rs->tag2*p->tag1%P+p->tag2)%P;
p->ls->tag1=p->ls->tag1*p->tag1%P;
p->ls->tag2=((p->ls->tag2*p->tag1)%P+p->tag2)%P;
p->tag1=1; p->tag2=0;
return;
}
LL query(node* p,int l,int r){
if(p->l==l && p->r==r){
return getData(p);
}
pushdown(p);
int mid=(p->l+p->r)/2;
if(r<=mid) return query(p->ls,l,r);
if(l>mid) return query(p->rs,l,r);
return (query(p->ls,l,mid)+query(p->rs,mid+1,r))%P;
}
void changePlus(node* p,int l,int r,LL dv){
if(p->l==l && p->r==r){
p->tag2=(p->tag2+dv)%P;
return;
}
pushdown(p);
int mid=(p->l+p->r)/2;
if(r<=mid) changePlus(p->ls,l,r,dv);
else if(l>mid) changePlus(p->rs,l,r,dv);
else{
changePlus(p->ls,l,mid,dv);
changePlus(p->rs,mid+1,r,dv);
}
p->d=(getData(p->ls)+getData(p->rs))%P;
}
void changeTimes(node* p,int l,int r,LL dv){
if(p->l==l && p->r==r){
p->tag1=p->tag1*dv%P;
p->tag2=p->tag2*dv%P;
return;
}
pushdown(p);
int mid=(p->l+p->r)/2;
if(r<=mid) changeTimes(p->ls,l,r,dv);
else if(l>mid) changeTimes(p->rs,l,r,dv);
else{
changeTimes(p->ls,l,mid,dv);
changeTimes(p->rs,mid+1,r,dv);
}
p->d=( getData(p->ls)+getData(p->rs) )%P;
}
int main(){
cin>>N>>M>>P;
for(int i=1;i<=N;i++) cin>>a[i];
node* root=buildT(1,N);
for(int i=1;i<=M;i++){
int op,x,y; cin>>op>>x>>y;
if(op==1){
LL k; cin>>k;
changeTimes(root,x,y,k);
}
else if(op==2){
LL k; cin>>k;
changePlus(root,x,y,k);
}
else cout<<query(root,x,y)<<endl;
}
return 0;
}