数列游戏
Description
给定一个长度为N的序列,首先进行A次操作,每次操作在Li和Ri这个区间加上一个数Ci。然后有B次询问,每次询问Li到Ri的区间和。
初始序列都为0。
Input
对于每组数据,第一行三个整数N A B。(1<=N<=1000000,1<=A<=N,A<=B<=N)
接下来A行,每行三个数Li Ri Ci。(1<=Li<=N,Li<=Ri<=N,|Ci|<=100000000000000)。
接下来B行,每行两个数 Li Ri。范围同上。
Output
对于每次询问,输出一行一个整数。因为最后的结果可能很大,请对结果mod 1000000007。
Sample Input
5 1 1 1 3 1 1 4
Sample Output
3
思路:
如果我们按照暴力的方法来,每次修改都对r到l区间增加c的话,那么答案是肯定的:Time Limit Exceeded(时间超时),注意一下啊,这道题不是边增加区间的值边询问的,而是将所有l到r的区间都增加了之后,才开始询问的,所以,我们只需要维护一个差分数组就可以了。
怎么维护呢?其实很简单,只需要O(2)的时间,如果将L到R的区间增加C,那么我们只需要对差分数组D的D[L]和D[R+1]进行操作就可以了,怎么操作呢?我们来举一个例子:A为1,2,3,4,5,那么A的差分数组D应该是1,1,1,1,1,然后我们对区间[2,4](2,3,4)进行加2操作,A就变成了1,4,5,6,5,现在的D是什么呢?D就是1,3,1,1,-1,发现什么没有?
在增加了区间值之后,D只改变了两个地方,那就是D[L]和D[R+1]变了,怎么变了呢?D[L]+=2了,D[R]-=2,对不对?这个2是什么呢?就是我们将区间里面加的C啊,这样子,我们的差分数组就维护成功了,但是为什么是这样子呢?我们来证明一下:
我们将A[L]到A[R]的区间内加上C,在D数组里(差分数组),从1~L-1, 是不会改变的,因为D[L-1]=A[L-1]-A[L-2],A[L-1]和A[L-2]的值是并没有改变的。
我们来看看D[L]=A[L]-A[L-1],A[L-1]是没有变的,但是A[L]增加了C,所以维护差分数组之后D[L]+=C,我们来看L+1~R,D[L+1]=A[L+1]-A[L]的值和区间没有增加的时候是一样的,为什么呢?因为A[L+1]和A[L]都加了C,所以减起来还是差不变,D[R]也是一样。
我们来看看D[R+1]=A[R+1]-A[R],因为A[R]加了C,相当于多减了一个C,所以维护差分数组是D[R+1]-=3.
求出了维护之后的差分数组D,我们可以重新求出新的数组A,怎么求呢?差分数组求出的是前后两个值的差,我们只需要将A[i]定义为A[i-1]+D[i]就可以了,这样重新求出了A这个数组,那我们怎么求A数组的区间和呢?这个就可以用到前缀和算法了,用S求出A的前缀和S[i]=S[i-1]+A[i],S[0]=A[0]=0,这样我们最后输出S[R]-S[L-1]就可以了!(因为S[R]求的是A[1]加到A[R]的,A[L-1]是A[1]加到A[L-1]的,两者相减,就是S[L]加到S[R]的值了)
完整代码:
#include<bits/stdc++.h>
using namespace std;
const int N=100000;
const int mod=1e9+7;
int main(){
int n,m,k;
int d[N],a[N],s[N];
memset(d,0,sizeof(d));
memset(a,0,sizeof(a));
memset(s,0,sizeof(s));
scanf("%d%d%d",&n,&m,&k);
long long sum=0;
for(int i=1,x,y,z;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
d[x]=(d[x]+z)%mod;
d[y+1]=(d[y+1]-z)%mod;
}
for(int i=1;i<=n;i++)
a[i]=(d[i]+a[i-1])%mod;
for(int i=1;i<=n;i++)
s[i]=(s[i-1]+a[i])%mod;
for(int i=1,x,y;i<=k;i++){
scanf("%d%d",&x,&y);
cout<<s[y-1]-s[x-2]<<endl;
}
return 0;
}
总结:
此题是差分算法的入门题,算是很简单的了,需要熟练掌握前缀和的知识后学习。