题目来源:洛谷
题目背景
无聊的YYB总喜欢搞出一些正常人无法搞出的东西。有一天,无聊的YYB想出了一道无聊的题:无聊的数列。。。(K峰:这题不是傻X题吗)
题目描述
维护一个数列{a[i]},支持两种操作:
1、1 L R K D:给出一个长度等于R-L+1的等差数列,首项为K,公差为D,并将它对应加到a[L]~a[R]的每一个数上。即:令a[L]=a[L]+K,a[L+1]=a[L+1]+K+D,
a[L+2]=a[L+2]+K+2D……a[R]=a[R]+K+(R-L)D。
2、2 P:询问序列的第P个数的值a[P]。
输入输出格式
输入格式:
第一行两个整数数n,m,表示数列长度和操作个数。
第二行n个整数,第i个数表示a[i](i=1,2,3…,n)。
接下来的m行,表示m个操作,有两种形式:
1 L R K D
2 P 字母意义见描述(L≤R)。
输出格式:
对于每个询问,输出答案,每个答案占一行。
输入输出样例
说明
数据规模:
0≤n,m≤100000
|a[i]|,|K|,|D|≤200
Hint:
有没有巧妙的做法?
解析:
这道题我跟一条题解想到一块去了。
具体做法大概就是维护一条差分序列。为啥要维护差分序列呢?这个是可以从等差数列这个点看出来的。
等差数列的性质决定了它可以用差分序列维护,这就保证了线段树的可结合性。
跟不要说,题目写的很清楚的一则这个玩意:
a[L]=a[L]+K,a[L+1]=a[L+1]+K+D,
a[L+2]=a[L+2]+K+2D……a[R]=a[R]+K+(R-L)D。
更是侧面应证了我们的思路的正确性。
打几个草稿就能发现此差分数列维护规律:
若差分序列为 d[],执行操作 1 l r k d
-
d[l]=d[l]+k;
-
d[i]=d[i]+d,l<i<=r;
-
d[r+1]=d[r+1]-(k+d*(r-l));
至于具体原因,详见差分序列性质,思考一下很容易明白。
参考代码:
#include<cstdio> #include<iostream> #include<algorithm> #define N 100010 #define ll long long using namespace std; ll a[N],b[N]; struct segmentree{ int l,r; ll dat,add; }t[N<<2]; void build(int p,int l,int r) { t[p].l=l;t[p].r=r; if(l==r){ t[p].dat=b[l]; return; } int mid=(l+r)>>1; build(p<<1,l,mid); build(p<<1|1,mid+1,r); t[p].dat=t[p<<1].dat+t[p<<1|1].dat; } void pushdown(int p) { if(t[p].add) { t[p<<1].add+=t[p].add; t[p<<1|1].add+=t[p].add; t[p<<1].dat+=t[p].add*(t[p<<1].r-t[p<<1].l+1); t[p<<1|1].dat+=t[p].add*(t[p<<1|1].r-t[p<<1|1].l+1); t[p].add=0; } } void change(int p,int l,int r,ll val) { if(l<=t[p].l&&t[p].r<=r){ t[p].add+=val; t[p].dat+=val*(t[p].r-t[p].l+1); return; } pushdown(p); int mid=(t[p].l+t[p].r)>>1; if(l<=mid) change(p<<1,l,r,val); if(r>mid) change(p<<1|1,l,r,val); t[p].dat=t[p<<1].dat+t[p<<1|1].dat; } ll query(int p,int l,int r) { if(l<=t[p].l&&t[p].r<=r) return t[p].dat; pushdown(p); int mid=(t[p].l+t[p].r)>>1; ll val=0; if(l<=mid) val+=query(p<<1,l,r); if(r>mid) val+=query(p<<1|1,l,r); return val; } int main() { int n,m,flag,l,r; ll k,d; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%lld",&a[i]); b[i]=a[i]-a[i-1];//差分 } build(1,1,n); while(m--) { scanf("%d",&flag); if(flag==1){ scanf("%d%d%lld%lld",&l,&r,&k,&d); //这个地方注意要特判 //当需要修改的差分序列较特殊时,需要特殊处理 if(r==n&&l==r){ change(1,l,l,k); } else if(r>=n){ change(1,l,l,k); change(1,l+1,r,d); } else{ change(1,l,l,k); change(1,l+1,r,d); change(1,r+1,r+1,-(k+d*(r-l)));//注意不用+1,由于差分序列改变的是l+1~r的值 } } else{ scanf("%d",&r); printf("%lld\n",query(1,1,r)); } } return 0; }
2019-05-17 14:23:49