维护一个数列{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]。
这道题很显然可以用线段树来做。
但这道题的区间修改是加上一个等差数列,所以直接硬搞的话会比较困难,需要寻找其他的方法。
这时就很容易能就想到差分了。因为等差数列满足An=An-1+d,它们的差不变,所以用差分来做会十分简单。
我们可以将修改分为3部分。
s u m [ l ] = s u m [ l ] + k sum[l]=sum[l]+k sum[l]=sum[l]+k
s u m [ i ] = s u m [ i − 1 ] + d , i ∈ ( l , r ] sum[i]=sum[i-1]+d,i\in(l,r] sum[i]=sum[i−1]+d,i∈(l,r]
s u m [ r + 1 ] = s u m [ r + 1 ] − ( k + ( r − l ) ∗ d ) ) sum[r+1]=sum[r+1]-(k+(r-l)*d)) sum[r+1]=sum[r+1]−(k+(r−l)∗d))
对于每次询问第p个位置的值,答案即为
a [ p ] + ∑ i = 1 p s u m [ i ] a[p]+\sum_{i=1}^p sum[i] a[p]+i=1∑psum[i]
总结一下,就是要我们对差分数组进行区间修改+区间求值。
这道题在修改的时候要特判两种情况
这种情况不需要对上述三种中区间的中间部分进行修改。因为此时l+1>r,会越界
这种情况不需要对上述三种中区间右端点右边一位进行修改。因为此时r+1>n,也会越界
#include
#define maxn 100009
using namespace std;
int n,m,a[maxn];
struct node
{
int l,r,sum,mk;
}tr[maxn*5];
void Build_tree (int x,int l,int r)
{
tr[x].l=l,tr[x].r=r;
if (l==r)
return;
int mid=(l+r)>>1,ls=x<<1,rs=(x<<1)+1;
Build_tree(ls,l,mid);
Build_tree(rs,mid+1,r);
}
void Release (int x)
{
if (tr[x].mk)
{
int ls=x<<1,rs=(x<<1)+1;
tr[ls].mk+=tr[x].mk;
tr[rs].mk+=tr[x].mk;
tr[ls].sum+=tr[x].mk*(tr[ls].r-tr[ls].l+1);
tr[rs].sum+=tr[x].mk*(tr[rs].r-tr[rs].l+1);
tr[x].mk=0;
}
}
void Modify (int x,int l,int r,int d)
{
Release(x);
if (tr[x].l>=l&&tr[x].r<=r)
{
tr[x].sum+=d*(tr[x].r-tr[x].l+1);
tr[x].mk+=d;
return;
}
int mid=(tr[x].l+tr[x].r)>>1,ls=x<<1,rs=(x<<1)+1;
if (l<=mid)
Modify(ls,l,r,d);
if (r>=mid+1)
Modify(rs,l,r,d);
tr[x].sum=tr[ls].sum+tr[rs].sum;
}
long long Query (int x,int l,int r)
{
Release(x);
if (tr[x].l>=l&&tr[x].r<=r)
return tr[x].sum;
int mid=(tr[x].l+tr[x].r)>>1,ls=x<<1,rs=(x<<1)+1,ans=0;
if (l<=mid)
ans+=Query(ls,l,r);
if (r>=mid+1)
ans+=Query(rs,l,r);
return ans;
}
int main ()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
Build_tree(1,1,n);
for (int i=1;i<=m;i++)
{
int l,r,k,op,d,p;
scanf("%d",&op);
if (op==1)
{
scanf("%d%d%d%d",&l,&r,&k,&d);
Modify(1,l,l,k);//第一种修改
if (l!=r)
Modify(1,l+1,r,d);//第二种修改
if (r!=n)
Modify(1,r+1,r+1,-(k+(r-l+1-1)*d));//第三种修改
}
if (op==2)
{
scanf("%d",&p);
printf("%d\n",a[p]+Query(1,1,p));
}
}
return 0;
}
这道题一开始我居然傻傻地想要直接修改,亏最后还是转过弯来了。太长时间不写,第一次交了后才发现数组开小了。真的菜啊QAQ