区间问题,肯定是线段树了,但是区间加的是一个等差数列,怎么办呢
我们可以通过差分来维护。
蛤是差分?
搞一个数组专门差分,在数组中记录对于l~r的区间加x,在l位置加上x,在r+1位置减去x。
当查询某个数值时,该位置上的数加上差分数组中1~该位置的前缀和,自己出组数试一下发现这样是对的
我们线段树刚好可以区间修改和区间求和,所以这题要用到线段树维护差分
对于首项,我们在线段树的l位置直接加首项,在l+1~r位置区间修改,每个位置加上d,在r+1位置减去等差数组中
的最后一项也就是a1+(r-l)d,这样我们的区间加等差数列就维护好了。
单点查询时,查询线段树中1~l的值加上原数组中a[l]的值。
然后特别注意的是r+1>n的情况我们不必再修改,会RE
对于l+1>r的情况,只需在l位置加上首项,l+1位置减去首项。
#include<iostream> #include<cstdio> #define lson k<<1,l,mid #define rson k<<1|1,mid+1,r #define ls k<<1 #define rs k<<1|1 #define sum(rt) tr[rt].sum #define laz(rt) tr[rt].laz #define mid ((l+r)>>1) using namespace std; const int maxn=100005; int n,m,a[maxn]; struct node{ int sum,laz; }tr[maxn<<2]; inline int read(){ int s=0,w=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();} return s*w; } inline void update(int k){ sum(k)=sum(ls)+sum(rs); } inline void down(int k,int l,int r){ sum(ls)+=laz(k)*(mid-l+1); laz(ls)+=laz(k); sum(rs)+=laz(k)*(r-mid); laz(rs)+=laz(k); laz(k)=0; } void change(int k,int l,int r,int x,int y,int val){ if(x<=l&&y>=r){ sum(k)+=val*(r-l+1); laz(k)+=val; return ; } if(laz(k)!=0)down(k,l,r); if(x<=mid)change(lson,x,y,val); if(y>mid)change(rson,x,y,val); update(k); } int ask(int k,int l,int r,int x,int y){ if(l==x&&y==r){ return sum(k); } if(laz(k)!=0)down(k,l,r); if(y<=mid)return ask(lson,x,y); else if(x>mid)return ask(rson,x,y); else return ask(lson,x,mid)+ask(rson,mid+1,y); } int main(){ n=read();m=read(); for(int i=1;i<=n;++i) a[i]=read(); int opt,l,r,k,d; while(m--){ opt=read(); if(opt==1){ l=read();r=read();k=read();d=read(); change(1,1,n,l,l,k); if(l!=r)change(1,1,n,l+1,r,d); if(r+1<=n)change(1,1,n,r+1,r+1,-(k+(r-l)*d)); } else { l=read(); printf("%d ",ask(1,1,n,1,l)+a[l]); } } return 0; }