题解 洛谷P1438 无聊的数列

题解 洛谷P1438 无聊的数列

Date 2019.8,2


题目大意

维护一个数列{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部分。

1.对于区间左端点l:

s u m [ l ] = s u m [ l ] + k sum[l]=sum[l]+k sum[l]=sum[l]+k

2.对于区间的中间部分l+1~r:

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[i1]+d,i(l,r]

3.对于区间右端点右边一位r+1:

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+(rl)d))
对于每次询问第p个位置的值,答案即为
a [ p ] + ∑ i = 1 p s u m [ i ] a[p]+\sum_{i=1}^p sum[i] a[p]+i=1psum[i]
总结一下,就是要我们对差分数组进行区间修改+区间求值

注意

这道题在修改的时候要特判两种情况

1.l=r

这种情况不需要对上述三种中区间的中间部分进行修改。因为此时l+1>r,会越界

2.r=n

这种情况不需要对上述三种中区间右端点右边一位进行修改。因为此时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

你可能感兴趣的:(题解,题解)