LUOGU P1438 无聊的数列 (差分+线段树)
解题思路
区间加等差数列+单点询问,用差分+线段树解决,线段树里维护的就是差分数组,区间加等差数列相当于在差分序列中l位置处+首项的值,r+1位置处-末项的值,中间加公差的值,然后单点询问就相当于在差分数列中求前缀和。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> using namespace std; const int MAXN = 100005; typedef long long LL; inline int rd(){ int x=0,f=1;char ch=getchar(); while(!isdigit(ch)) {f=ch=='-'?0:1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f?x:-x; } int n,m,a[MAXN],c[MAXN]; LL sum[MAXN<<2],lazy[MAXN<<2]; void pushdown(int x,int ln,int rn){ lazy[x<<1]+=lazy[x];lazy[x<<1|1]+=lazy[x]; sum[x<<1]+=ln*lazy[x];sum[x<<1|1]+=rn*lazy[x]; lazy[x]=0; } void build(int x,int l,int r){ if(l==r){sum[x]=c[l];return;} int mid=(l+r)>>1; build(x<<1,l,mid);build(x<<1|1,mid+1,r); sum[x]=sum[x<<1]+sum[x<<1|1]; } void update(int x,int l,int r,int L,int R,int w){ if(L<=l && r<=R) {sum[x]+=(r-l+1)*w;lazy[x]+=w;return;} int mid=(l+r)>>1;if(lazy[x]) pushdown(x,mid-l+1,r-mid); if(L<=mid) update(x<<1,l,mid,L,R,w); if(mid<R) update(x<<1|1,mid+1,r,L,R,w); sum[x]=sum[x<<1]+sum[x<<1|1]; } LL query(int x,int l,int r,int L,int R){ if(L<=l && r<=R) return sum[x]; int mid=(l+r)>>1;LL ret=0;if(lazy[x]) pushdown(x,mid-l+1,r-mid); if(L<=mid) ret+=query(x<<1,l,mid,L,R); if(mid<R) ret+=query(x<<1|1,mid+1,r,L,R); return ret; } int main(){ n=rd(),m=rd(); for(int i=1;i<=n;i++){a[i]=rd();c[i]=a[i]-a[i-1];} build(1,1,n); int op,l,r,k,d; while(m--){ op=rd();l=rd(); if(op==1){ r=rd(),k=rd(),d=rd();update(1,1,n,l,l,k); if(r!=n) update(1,1,n,r+1,r+1,-(k+(r-l)*d)); if(l!=r) update(1,1,n,l+1,r,d); } else printf("%lld\n",query(1,1,n,1,l)); } return 0; }