将写一篇比较长的博客来系统的学习下线段树:群友Limit的线段树题单以及线段树分治的某些题单
update:群友Limit的线段树题单two
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e5+5; ll Tree[N<<4],w[N],lazy[N<<4]; //分别表示该节点的下的权值和是多少, void pushdown(int u,int l,int r) { int mid=(l+r)>>1; Tree[u<<1]+=(mid-l+1)*lazy[u]; Tree[u<<1|1]+=(r-mid)*lazy[u]; lazy[u<<1]+=lazy[u]; lazy[u<<1|1]+=lazy[u]; lazy[u]=0; } void modify(int u,int l,int r,int L,int R,int val)//在[l,r]中更新[L,R]. { int mid=(l+r)>>1; if(L>r||R<l) return; if(L<=l&&r<=R) //这个区间信息可以保留. { lazy[u]+=val; Tree[u]+=(r-l+1)*val; return; } pushdown(u,l,r); modify(u<<1,l,mid,L,R,val); modify(u<<1|1,mid+1,r,L,R,val); Tree[u]=Tree[u<<1]+Tree[u<<1|1]; } ll query(int u,int l,int r,int L,int R)//在[l,r]中查询[L,R]. { int mid=(l+r)>>1; if(L>r||R<l) return 0; pushdown(u,l,r); if(L<=l&&r<=R) //这个区间信息可以保留. { return Tree[u]; } return query(u<<1,l,mid,L,R)+query(u<<1|1,mid+1,r,L,R); } void build(int u,int l,int r) { if(l==r) { Tree[u]=w[l]; return; }int mid=(l+r)>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); Tree[u]=Tree[u<<1]+Tree[u<<1|1]; } int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%lld",&w[i]); }build(1,1,n); for(int i=1;i<=m;i++) { int op,x,y,k; scanf("%d",&op); if(op==1) { scanf("%d%d%d",&x,&y,&k); modify(1,1,n,x,y,k); } else { scanf("%d%d",&x,&y); printf("%lld\n",query(1,1,n,x,y)); } } return 0; }
P6492 [COCI2010-2011#6] STEP
吐槽:为什么我的代码如此优美呢?2333
#include <bits/stdc++.h> using namespace std; const int N=2e5+50; struct Tree{ int l,r,L,R,lans,rans,ans;//l,r表示管辖的范围.L,R表示该节点左右两端的值.ans表示最长的长度. }tr[N<<2]; void push_up(int u) { int ls=(u<<1),rs=(u<<1|1); tr[u].ans=max(tr[ls].ans,tr[rs].ans); tr[u].lans=tr[ls].lans; tr[u].rans=tr[rs].rans; if(tr[ls].R^tr[rs].L) { tr[u].ans=max(tr[u].ans,tr[ls].rans+tr[rs].lans); if(tr[ls].lans==(tr[ls].r-tr[ls].l+1)) tr[u].lans=max(tr[u].lans,tr[ls].lans+tr[rs].lans); if(tr[rs].rans==(tr[rs].r-tr[rs].l+1)) tr[u].rans=max(tr[u].rans,tr[rs].rans+tr[ls].rans); } tr[u].l=tr[ls].l,tr[u].r=tr[rs].r; tr[u].L=tr[ls].L,tr[u].R=tr[rs].R; } void modify(int u,int pos)//在当前管辖的区间更新某个位子. { int ls=(u<<1),rs=(u<<1|1); if(pos>tr[u].r||pos<tr[u].l) return; if(tr[u].l==tr[u].r&&tr[u].l==pos) { int odd=tr[u].L; tr[u].L=tr[u].R=(!odd); return; } modify(ls,pos); modify(rs,pos); push_up(u); } void build(int u,int l,int r) { if(l==r) { tr[u].l=tr[u].r=l; tr[u].L=tr[u].R=0; tr[u].lans=tr[u].rans=1; tr[u].ans=1;return; }int mid=(l+r)>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); push_up(u); } int main() { int n,q; scanf("%d%d",&n,&q); build(1,1,n); while(q--) { int x; scanf("%d",&x); modify(1,x); printf("%d\n",tr[1].ans); } return 0; }
#include <bits/stdc++.h> using namespace std; #define int long long const int N=1e5+50; struct Tree{ int l,r,mul,add,sum;//当前节点所管辖的左右区间,mul,add延迟标记,区间总和sum. }tr[N<<2]; int n,m,p,w[N]; void push_down(int u) { tr[u<<1].sum=(tr[u].mul*tr[u<<1].sum+(tr[u].add*(tr[u<<1].r-tr[u<<1].l+1))%p)%p; tr[u<<1|1].sum=(tr[u].mul*tr[u<<1|1].sum+(tr[u].add*(tr[u<<1|1].r-tr[u<<1|1].l+1))%p)%p; tr[u<<1].mul=(tr[u<<1].mul*tr[u].mul)%p; tr[u<<1|1].mul=(tr[u<<1|1].mul*tr[u].mul)%p; tr[u<<1].add=(tr[u<<1].add*tr[u].mul+tr[u].add)%p; tr[u<<1|1].add=(tr[u<<1|1].add*tr[u].mul+tr[u].add)%p; tr[u].add=0,tr[u].mul=1; } void push_up(int u) { tr[u].l=tr[u<<1].l;tr[u].r=tr[u<<1|1].r; tr[u].sum=(tr[u<<1].sum+tr[u<<1|1].sum)%p; } void add(int u,int l,int r,int val) { if(r<tr[u].l||l>tr[u].r) return; if(l<=tr[u].l&&tr[u].r<=r) { tr[u].add+=val; tr[u].add%=p; tr[u].sum=(tr[u].sum+val*(tr[u].r-tr[u].l+1))%p; return; } push_down(u); add(u<<1,l,r,val); add(u<<1|1,l,r,val); push_up(u); } void mul(int u,int l,int r,int val) { if(r<tr[u].l||l>tr[u].r) return; if(l<=tr[u].l&&tr[u].r<=r) { tr[u].add*=val; tr[u].add%=p; tr[u].mul*=val; tr[u].mul%=p; tr[u].sum=(tr[u].sum*val)%p; return; } push_down(u); mul(u<<1,l,r,val); mul(u<<1|1,l,r,val); push_up(u); } void build(int u,int l,int r) { tr[u].mul=1; if(l==r) { tr[u].l=tr[u].r=l; tr[u].sum=w[l]%p; return; }int mid=(l+r)>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); push_up(u); } int query(int u,int l,int r) { if(r<tr[u].l||l>tr[u].r) return 0; if(l<=tr[u].l&&tr[u].r<=r) { return tr[u].sum%p; }push_down(u); return (query(u<<1,l,r)+query(u<<1|1,l,r))%p; } signed main() { scanf("%lld%lld%lld",&n,&m,&p); for(int i=1;i<=n;i++) scanf("%lld",&w[i]); build(1,1,n); while(m--) { int op,x,y,k;scanf("%lld",&op); if(op==1) { scanf("%lld%lld%lld",&x,&y,&k); mul(1,x,y,k); } else if(op==2) { scanf("%lld%lld%lld",&x,&y,&k); add(1,x,y,k); } else { scanf("%lld%lld",&x,&y); printf("%lld\n",query(1,x,y)); } } }
下面那个和这个一样的...
P2023 [AHOI2009] 维护序列
#include <bits/stdc++.h> using namespace std; #define int long long const int N=1e5+50; struct Tree{ int l,r,mul,add,sum;//当前节点所管辖的左右区间,mul,add延迟标记,区间总和sum. }tr[N<<2]; int n,m,p,w[N]; void push_down(int u) { tr[u<<1].sum=(tr[u].mul*tr[u<<1].sum+(tr[u].add*(tr[u<<1].r-tr[u<<1].l+1))%p)%p; tr[u<<1|1].sum=(tr[u].mul*tr[u<<1|1].sum+(tr[u].add*(tr[u<<1|1].r-tr[u<<1|1].l+1))%p)%p; tr[u<<1].mul=(tr[u<<1].mul*tr[u].mul)%p; tr[u<<1|1].mul=(tr[u<<1|1].mul*tr[u].mul)%p; tr[u<<1].add=(tr[u<<1].add*tr[u].mul+tr[u].add)%p; tr[u<<1|1].add=(tr[u<<1|1].add*tr[u].mul+tr[u].add)%p; tr[u].add=0,tr[u].mul=1; } void push_up(int u) { tr[u].l=tr[u<<1].l;tr[u].r=tr[u<<1|1].r; tr[u].sum=(tr[u<<1].sum+tr[u<<1|1].sum)%p; } void add(int u,int l,int r,int val) { if(r<tr[u].l||l>tr[u].r) return; if(l<=tr[u].l&&tr[u].r<=r) { tr[u].add+=val; tr[u].add%=p; tr[u].sum=(tr[u].sum+val*(tr[u].r-tr[u].l+1))%p; return; } push_down(u); add(u<<1,l,r,val); add(u<<1|1,l,r,val); push_up(u); } void mul(int u,int l,int r,int val) { if(r<tr[u].l||l>tr[u].r) return; if(l<=tr[u].l&&tr[u].r<=r) { tr[u].add*=val; tr[u].add%=p; tr[u].mul*=val; tr[u].mul%=p; tr[u].sum=(tr[u].sum*val)%p; return; } push_down(u); mul(u<<1,l,r,val); mul(u<<1|1,l,r,val); push_up(u); } void build(int u,int l,int r) { tr[u].mul=1; if(l==r) { tr[u].l=tr[u].r=l; tr[u].sum=w[l]%p; return; }int mid=(l+r)>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); push_up(u); } int query(int u,int l,int r) { if(r<tr[u].l||l>tr[u].r) return 0; if(l<=tr[u].l&&tr[u].r<=r) { return tr[u].sum%p; }push_down(u); return (query(u<<1,l,r)+query(u<<1|1,l,r))%p; } signed main() { scanf("%lld%lld",&n,&p); for(int i=1;i<=n;i++) scanf("%lld",&w[i]); build(1,1,n); scanf("%lld",&m); while(m--) { int op,x,y,k;scanf("%lld",&op); if(op==1) { scanf("%lld%lld%lld",&x,&y,&k); mul(1,x,y,k); } else if(op==2) { scanf("%lld%lld%lld",&x,&y,&k); add(1,x,y,k); } else { scanf("%lld%lld",&x,&y); printf("%lld\n",query(1,x,y)); } } }
P1637 三元上升子序列
这题用树状数组更好写,我主要是练下线段树而已...中间还是出现了小小的bug,我不行i...
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=3e4+40; int w[N]; int ls[N]; struct Tree{ int l,r,sum; }tr[2][N<<2];//开两颗线段树维护前面有多少比它小的,后面有多少比它大的. void build(int u,int l,int r,int op) { tr[op][u].sum=0; tr[op][u].l=l,tr[op][u].r=r; if(l==r) return; int mid=(l+r)>>1; build(u<<1,l,mid,op); build(u<<1|1,mid+1,r,op); } void add(int u,int pos,int val,int op)//使得单点加上多少. { if(pos>tr[op][u].r||pos<tr[op][u].l) return; if(pos==tr[op][u].l&&pos==tr[op][u].r) { tr[op][u].sum+=val; return; } add(u<<1,pos,val,op); add(u<<1|1,pos,val,op); tr[op][u].sum=tr[op][u<<1].sum+tr[op][u<<1|1].sum; } int query(int u,int l,int r,int op)//查询区间答案. { if(l>tr[op][u].r||r<tr[op][u].l) return 0; if(l<=tr[op][u].l&&r>=tr[op][u].r) { return tr[op][u].sum; }return query(u<<1,l,r,op)+query(u<<1|1,l,r,op); } int main() { int n; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&w[i]); ls[i]=w[i]; }sort(ls+1,ls+1+n); build(1,1,n,0); build(1,1,n,1); for(int i=1;i<=n;i++) { w[i]=lower_bound(ls+1,ls+1+n,w[i])-ls; add(1,w[i],1,1); } ll ans=0; for(int i=1;i<=n;i++) { add(1,w[i],-1,1); if(w[i]>1&&w[i]<n) ans+=1ll*query(1,1,w[i]-1,0)*query(1,w[i]+1,n,1); add(1,w[i],1,0); }printf("%lld\n",ans); return 0; }
CF915E Physical Education Lessons
动态开点.
#include <bits/stdc++.h> using namespace std; const int N=1e7+5e6; int lazy[N],ls[N],rs[N],sum[N],root,cnt;//线段树节点u的左右两个端点.以及这个端点的权值. void pushdown(int u,int l,int r) { if(~lazy[u]) { if(!ls[u]) ls[u]=++cnt; if(!rs[u]) rs[u]=++cnt; int mid=(l+r)>>1; sum[ls[u]]=(mid-l+1)*lazy[u]; sum[rs[u]]=(r-mid)*lazy[u]; lazy[ls[u]]=lazy[rs[u]]=lazy[u]; lazy[u]=-1; } } void modify(int &u,int l,int r,int L,int R,int val) { if(!u) u=++cnt; if(L<=l&&r<=R) { sum[u]=val*(r-l+1); lazy[u]=val; return; }int mid=(l+r)>>1; pushdown(u,l,r); if(L<=mid) modify(ls[u],l,mid,L,R,val); if(R>mid) modify(rs[u],mid+1,r,L,R,val); sum[u]=sum[ls[u]]+sum[rs[u]]; } int main() { int n,q; scanf("%d%d",&n,&q); //我们假设工作日为0,非工作日为1.这样比较方便. memset(lazy,-1,sizeof lazy); modify(root,1,n,1,n,0); while(q--) { int L,R,k; scanf("%d%d%d",&L,&R,&k); modify(root,1,n,L,R,2-k);//区间修改成2-k. printf("%d\n",n-sum[1]);//输出答案. } return 0; }
P3384 【模板】轻重链剖分
树链剖分.
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e5+500; ll n,m,r,p; ll w[N]; vector<int>v[N]; ll dep[N],siz[N],dfn[N],f[N]; //跑出深度,子树大小,重儿子,父亲. void dfs1(int u,int fa) { siz[u]=1;dep[u]=dep[fa]+1;f[u]=fa; int num=v[u].size(); for(int i=0;i<num;i++) { int son=v[u][i]; if(son==fa) continue; dfs1(son,u); siz[u]+=siz[son]; if(siz[son]>siz[dfn[u]]) dfn[u]=son; } } //跑出每个节点的下标以及处理它们的权值,然后统计重链的起点. ll id=0,idx[N],top[N],val[N]; void dfs2(int u,int topf) { idx[u]=++id; top[u]=topf; val[id]=w[u]; if(!dfn[u]) return; dfs2(dfn[u],topf); int num=v[u].size(); for(int i=0;i<num;i++) { int son=v[u][i]; if(idx[son]) continue; dfs2(son,son); } } //写一份有线段树区间修改,区间求和功能的树. struct Tree{ ll l,r,len,sum,lazy; }tr[N<<2]; void pushup(int u) { tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum; } void pushdown(int u) { tr[u<<1].sum=(tr[u<<1].sum+tr[u].lazy*tr[u<<1].len%p)%p; tr[u<<1|1].sum=(tr[u<<1|1].sum+tr[u].lazy*tr[u<<1|1].len%p)%p; tr[u<<1].lazy=(tr[u<<1].lazy+tr[u].lazy)%p; tr[u<<1|1].lazy=(tr[u<<1|1].lazy+tr[u].lazy)%p; tr[u].lazy=0; } void build(int u,int l,int r) { tr[u].len=(r-l+1);tr[u].l=l;tr[u].r=r; if(l==r) { tr[u].l=tr[u].r=l; tr[u].sum=val[l]; return; }int mid=(l+r)>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); pushup(u); } void add(int u,int L,int R,int k) { if(tr[u].r<L||R<tr[u].l) return; if(L<=tr[u].l&&tr[u].r<=R) { tr[u].sum=(tr[u].sum+k*(tr[u].len)%p)%p; tr[u].lazy=(tr[u].lazy+k)%p; return; }pushdown(u); add(u<<1,L,R,k); add(u<<1|1,L,R,k); pushup(u); } ll query(int u,int L,int R) { if(tr[u].r<L||R<tr[u].l) return 0; if(L<=tr[u].l&&tr[u].r<=R) return tr[u].sum%p; pushdown(u); return (query(u<<1,L,R)+query(u<<1|1,L,R))%p; } //实现v->u的修改以及查询操作. void Treeadd(int x,int y,int k) { while(top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]]) swap(x,y); add(1,idx[top[x]],idx[x],k); x=f[top[x]]; } if(dep[x]>dep[y]) swap(x,y); add(1,idx[x],idx[y],k); } ll Treesum(int x,int y) { ll ans=0; while(top[x]!=top[y]) { if(dep[top[x]]<dep[top[y]]) swap(x,y); ans=(ans+query(1,idx[top[x]],idx[x]))%p; x=f[top[x]]; } if(dep[x]>dep[y]) swap(x,y); ans=(ans+query(1,idx[x],idx[y]))%p; return ans; } int main() { scanf("%lld%lld%lld%lld",&n,&m,&r,&p); for(int i=1;i<=n;i++) scanf("%lld",&w[i]); for(int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); v[x].push_back(y); v[y].push_back(x); } dfs1(r,r); dfs2(r,r); build(1,1,n); while(m--) { int op,x,y,z; scanf("%d",&op); if(op==1) { scanf("%d%d%d",&x,&y,&z); Treeadd(x,y,z%p); } else if(op==2) { scanf("%d%d",&x,&y); printf("%lld\n",Treesum(x,y)); } else if(op==3) { scanf("%d%d",&x,&z); add(1,idx[x],idx[x]+siz[x]-1,z%p); } else { scanf("%d",&x); printf("%lld\n",query(1,idx[x],idx[x]+siz[x]-1)); } } return 0; }
P1531 I Hate It
简单的单点修改和区间查询吧..
#include <bits/stdc++.h> using namespace std; const int N=2e5+500; struct Tree{ int l,r,ans; }tr[N<<2]; int w[N]; void pushup(int u) { tr[u].ans=max(tr[u<<1].ans,tr[u<<1|1].ans); } void modify(int u,int l,int r,int val) { if(tr[u].r<l||tr[u].l>r) return; if(l<=tr[u].l&&tr[u].r<=r) { tr[u].ans=max(tr[u].ans,val); return; } modify(u<<1,l,r,val); modify(u<<1|1,l,r,val); pushup(u); } int query(int u,int l,int r) { if(tr[u].r<l||tr[u].l>r) return 0; if(l<=tr[u].l&&tr[u].r<=r) { return tr[u].ans; }return max(query(u<<1,l,r),query(u<<1|1,l,r)); } void build(int u,int l,int r) { tr[u].l=l;tr[u].r=r; if(l==r) { tr[u].ans=w[l]; return; }int mid=(l+r)>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); pushup(u); } int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&w[i]); build(1,1,n); while(m--) { char a;int b,c; cin>>a>>b>>c; if(a=='Q') { printf("%d\n",query(1,b,c)); } else { modify(1,b,b,c); } } return 0; }
P1276 校门外的树(增强版)
比模板还是多了些操作的...不错的题.
#include <bits/stdc++.h> using namespace std; const int N=1e5+50; int ans1=0,ans2=0; struct Tree{ int l,r,len,val;//1表示原来那颗大树,0表示没树苗,2表示有树苗,-1表示这个节点不能全部包括. }tr[N<<2]; void cut(int u,int l,int r) { if(tr[u].l>r||tr[u].r<l) return; if(tr[u].val!=-1) tr[u<<1].val=tr[u<<1|1].val=tr[u].val; if(l<=tr[u].l&&tr[u].r<=r&&tr[u].val!=-1) { if(tr[u].val==1) tr[u].val=0; if(tr[u].val==2) { ans1-=tr[u].len; ans2+=tr[u].len; tr[u].val=0; } return; } cut(u<<1,l,r); cut(u<<1|1,l,r); if(tr[u<<1].val!=tr[u<<1|1].val) tr[u].val=-1; else tr[u].val=tr[u<<1].val; } void plant(int u,int l,int r) { if(tr[u].l>r||tr[u].r<l) return; if(tr[u].val!=-1) tr[u<<1].val=tr[u<<1|1].val=tr[u].val; if(l<=tr[u].l&&tr[u].r<=r&&tr[u].val!=-1) { if(tr[u].val==0) ans1+=tr[u].len,tr[u].val=2; return; } plant(u<<1,l,r); plant(u<<1|1,l,r); if(tr[u<<1].val!=tr[u<<1|1].val) tr[u].val=-1; else tr[u].val=tr[u<<1].val; } void build(int u,int l,int r) { tr[u].l=l,tr[u].r=r,tr[u].val=1,tr[u].len=(r-l+1); if(l==r) return; int mid=(l+r)>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); } int main() { int n,m; scanf("%d%d",&n,&m);n++; build(1,1,n); for(int i=1;i<=m;i++) { int a,b,c;scanf("%d%d%d",&a,&b,&c); b++,c++; if(a==0) cut(1,b,c); else plant(1,b,c); }printf("%d\n%d\n",ans1,ans2);//校门外留下的树苗数目 种上又被拔掉的树苗数目 return 0; }
P1438 无聊的数列
真的菜,区间修改的线段树还是可以打出bug?
#include <bits/stdc++.h> using namespace std; const int N=1e5+500; typedef long long ll; ll a[N],w[N]; struct Tree{ ll l,r,len,sum,lazy; }tr[N<<2]; void pushup(int u) { tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum; } void pushdown(int u) { tr[u<<1].lazy+=tr[u].lazy; tr[u<<1|1].lazy+=tr[u].lazy; tr[u<<1].sum+=tr[u].lazy*tr[u<<1].len; tr[u<<1|1].sum+=tr[u].lazy*tr[u<<1|1].len; tr[u].lazy=0; } void add(int u,int l,int r,int val) { if(l>tr[u].r||r<tr[u].l) return; if(l<=tr[u].l&&tr[u].r<=r) { tr[u].lazy+=val; tr[u].sum+=tr[u].len*val; return; }pushdown(u); add(u<<1,l,r,val); add(u<<1|1,l,r,val); pushup(u); } ll query(int u,int l,int r) { if(l>tr[u].r||r<tr[u].l) return 0; pushdown(u); if(l<=tr[u].l&&tr[u].r<=r) { return tr[u].sum; } return query(u<<1,l,r)+query(u<<1|1,l,r); } void build(int u,int l,int r) { tr[u].l=l,tr[u].r=r,tr[u].len=r-l+1; tr[u].lazy=0; if(l==r) { tr[u].sum=w[l]; return; }int mid=(r+l)>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); pushup(u); } int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); if(i==1) w[i]=a[i]; else w[i]=a[i]-a[i-1]; }build(1,1,n); while(m--) { int op;int l,r,k,d; scanf("%d",&op); if(op==1) { scanf("%d%d%d%d",&l,&r,&k,&d); add(1,l,l,k); add(1,l+1,r,d); add(1,r+1,r+1,-(k+(r-l)*d)); } else { int p;scanf("%d",&p); printf("%lld\n",query(1,1,p)); } } return 0; }
P1471 方差
不难,但是也是值得学习的一题.
#include <bits/stdc++.h> using namespace std; const int N=1e5+500; struct Tree{ int l,r,len; double sum; double lazy; }_tr[N<<2],__tr[N<<2];//一个维护区间和,另外一个维护区间平方和. double w[N]; void pushup(int u) { _tr[u].sum=_tr[u<<1].sum+_tr[u<<1|1].sum; __tr[u].sum=__tr[u<<1].sum+__tr[u<<1|1].sum; } void pushdown(int u) { __tr[u<<1].lazy+=__tr[u].lazy; __tr[u<<1|1].lazy+=__tr[u].lazy; __tr[u<<1].sum+=_tr[u<<1].sum*2*__tr[u].lazy+__tr[u<<1].len*__tr[u].lazy*__tr[u].lazy; __tr[u<<1|1].sum+=_tr[u<<1|1].sum*2*__tr[u].lazy+__tr[u<<1|1].len*__tr[u].lazy*__tr[u].lazy; _tr[u<<1].lazy+=_tr[u].lazy; _tr[u<<1|1].lazy+=_tr[u].lazy; _tr[u<<1].sum+=_tr[u].lazy*_tr[u<<1].len; _tr[u<<1|1].sum+=_tr[u].lazy*_tr[u<<1|1].len; _tr[u].lazy=__tr[u].lazy=0; } void add(int u,int l,int r,double val) { if(l>_tr[u].r||r<_tr[u].l) return; if(_tr[u].l>=l&&_tr[u].r<=r) { __tr[u].lazy+=val; __tr[u].sum+=_tr[u].sum*2*val+_tr[u].len*val*val; _tr[u].lazy+=val; _tr[u].sum+=val*_tr[u].len; return; }pushdown(u); add(u<<1,l,r,val); add(u<<1|1,l,r,val); pushup(u); } double query(Tree tree[],int u,int l,int r) { if(l>tree[u].r||r<tree[u].l) return 0; if(l<=tree[u].l&&tree[u].r<=r) return tree[u].sum; pushdown(u); return query(tree,u<<1,l,r)+query(tree,u<<1|1,l,r); } void build(int u,int l,int r) { _tr[u].l=l,_tr[u].r=r; __tr[u].l=l,__tr[u].r=r; _tr[u].len=__tr[u].len=(r-l+1); _tr[u].lazy=__tr[u].lazy=0; if(l==r) { _tr[u].sum=w[l]; __tr[u].sum=w[l]*w[l]; return; }int mid=(l+r)>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); pushup(u); } int main() { int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%lf",&w[i]); build(1,1,n); int op,x,y; double k; while(m--) { scanf("%d",&op); if(op==1) { scanf("%d%d%lf",&x,&y,&k); add(1,x,y,k); } else if(op==2) { scanf("%d%d",&x,&y); printf("%.4f\n",query(_tr,1,x,y)/(y-x+1)); } else { scanf("%d%d",&x,&y); double age=query(_tr,1,x,y)/(y-x+1); printf("%.4f\n",fabs(age*age-query(__tr,1,x,y)/(y-x+1))); } } return 0; }