P3373 【模板】线段树 2
线段是是一个很好用的数据结构,特别是在于长长的板子
-----------------
链接:Miku
----------------
先来一份线段树1的代码
#include<iostream> #include<cstdio> #include<algorithm> int n,m; long long sum[400005], lazy[400005]; int f,x,y; long long k; void pushdown(int x, int L, int R){ if (lazy[x] != 0){ int mid = (L + R) >> 1; lazy[x << 1] += lazy[x]; lazy[x << 1 | 1] += lazy[x]; sum[x << 1] += lazy[x] * (mid - L + 1); sum[x << 1 | 1] += lazy[x] * (R - mid); lazy[x] = 0; } return; } void pushup(int x){ sum[x] = sum[x << 1] + sum[x << 1 | 1]; return; } void update(int x, int l, int r, int L, int R, long long d){ if (L <= l && r <= R){ lazy[x] += d; sum[x] += d * (r - l + 1); return; } int mid = (l + r) >> 1; pushdown(x, l, r); if (L <= mid) update(x << 1, l, mid, L, R, d); if (R > mid) update(x << 1 | 1, mid + 1, r, L, R, d); pushup(x); } long long query(int x, int l, int r, int L, int R){ if (L <= l && r <= R){ return sum[x]; } int mid = (l + r) >> 1; pushdown(x, l, r); long long ans = 0; if (L <= mid) ans += query(x << 1, l, mid, L, R); if (R > mid) ans += query(x << 1 | 1, mid + 1, r, L, R); return ans; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;++i){ scanf("%d",&x); update(1, 1, n, i, i, x); } for(int i=1;i<=m;++i){ scanf("%d",&f); if(f==1){ scanf("%d%d%lld",&x,&y,&k); update(1, 1, n, x, y, k); } else{ scanf("%d%d",&x,&y); printf("%lld\n", query(1, 1, n, x, y)); } } return 0; }
---------------
2的难度提升了,因为有两种不同的操作,加和乘。
我们不难发现,应该先乘后加,不然会碰到一个除法。
并且在乘的时候要给两个懒标记一块乘
注意懒标记的初始化和取模
------------
代码时间
(线段树这东西太长了)
#include<iostream> #include<cstdio> int n,m; long long mod = 571373; long long sum[400005],lazyadd[400005],lazymul[400005]; int x,y,f; long long k; void pushdown(int x,int l,int r){ if(lazymul[x]!=1){ int mid=(l+r)>>1; lazyadd[x<<1]*=lazymul[x];lazyadd[x<<1]%=mod;//运用乘法分配律,把加发标记乘一下 lazyadd[x<<1|1]*=lazymul[x];lazyadd[x<<1|1]%=mod; lazymul[x<<1]*=lazymul[x];lazymul[x<<1]%=mod;//乘法标记乘起来就行了 lazymul[x<<1|1]*=lazymul[x];lazymul[x<<1|1]%=mod; sum[x<<1]*=lazymul[x];sum[x<<1]%=mod;//区间和肯定是直接乘的 sum[x<<1|1]*=lazymul[x];sum[x<<1|1]%=mod; lazymul[x]=1; } if (lazyadd[x] != 0){//然后处理加法 int mid=(l+r)>>1; lazyadd[x<<1]+=lazyadd[x];lazyadd[x<<1]%=mod;//和1没大有区别 sum[x<<1]+=lazyadd[x]*(mid-l+1);sum[x<<1]%=mod; lazyadd[x<<1|1]+=lazyadd[x];lazyadd[x<<1|1]%=mod; sum[x<<1|1]+=lazyadd[x]*(r-mid);sum[x<<1|1]%=mod; lazyadd[x]=0; } return ; } void pushup(int x){1 sum[x]=sum[x<<1]+sum[x<<1|1]; sum[x]%=mod;//记得mod return ; } void update1(int x,int l,int r,int L,int R,long long d){//节约空间牺牲时间的行为,而且要小心大小写的问题、 //这个行为就是把这个点负责的左右区间放在函数里 //如果存在数组里的话 //用空间换时间 //简单的+ if(L<=l&&r<=R){ lazyadd[x]+=d; lazyadd[x]%=mod; sum[x]+=d*(r-l+1); sum[x]%=mod; return ; } int mid=(l+r)>>1; pushdown(x,l,r); if(L<=mid) update1(x<<1,l,mid,L,R,d); if(R>mid) update1(x<<1|1,mid+1,r,L,R,d); pushup(x); } void update2(int x,int l,int r,int L,int R,long long d){ if(L<=l&&r<=R){ lazyadd[x]*=d;//乘法分配律告诉我们,两个一起改 lazyadd[x]%=mod; lazymul[x]*=d; lazymul[x]%=mod; sum[x]*=d;//显然 sum[x]%=mod; return ; } int mid=(l+r)>>1; pushdown(x,l,r); if(L<=mid) update2(x<<1,l,mid,L,R,d); if(R>mid) update2(x<<1|1,mid+1,r,L,R,d); pushup(x); } long long query(int x,int l,int r,int L,int R){ if(L<=l&&r<=R){ return sum[x]; } long long ans=0; int mid=(l+r)>>1; pushdown(x,l,r); if(L<=mid) ans+=query(x<<1,l,mid,L,R),ans%=mod; if(R>mid) ans+=query(x<<1|1,mid+1,r,L,R),ans%=mod; return ans; } int main(){ scanf("%d%d%lld",&n,&m,&mod); for(int i=1;i<=400002;++i) lazymul[i]=1; for(int i=1;i<=n;++i){ scanf("%d",&x); update1(1,1,n,i,i,x); } for(int i=1;i<=m;++i){ scanf("%d",&f); if(f==1){ scanf("%d%d%lld",&x,&y,&k);//嗯,这里的数字有点反 update2(1,1,n,x,y,k); } if(f==2){ scanf("%d%d%lld",&x,&y,&k); update1(1,1,n,x,y,k); } if(f==3){ scanf("%d%d",&x,&y); printf("%lld\n",query(1,1,n,x,y)); } } return 0; }