差分数组+例题:Pikachu
…比赛遇到,我都不知道这是个啥…(太菜了太菜了)
一、差分数组的定义及用途
1.定义:
对于已知有n个元素的离线数列d,我们可以建立记录它每项与前一项差值的差分数组f:显然,f[1]=d[1]-0=d[1];对于整数i∈[2,n],我们让f[i]=d[i]-d[i-1]。
2.简单性质:
(1)计算数列各项的值:观察d[2]=f[1]+f[2]=d[1]+d[2]-d[1]=d[2]可知,数列第i项的值是可以用差分数组的前i项的和计算的,即d[i]=f[i]的前缀和。
(2)计算数列每一项的前缀和:第i项的前缀和即为数列前i项的和,那么推导可知
即可用差分数组求出数列前缀和;
3.用途:
(1)快速处理区间加减操作:
假如现在对数列中区间[L,R]上的数加上x,我们通过性质(1)知道,第一个受影响的差分数组中的元素为f[L],即令f[L]+=x,那么后面数列元素在计算过程中都会加上x;最后一个受影响的差分数组中的元素为f[R],所以令f[R+1]-=x,即可保证不会影响到R以后数列元素的计算。这样我们不必对区间内每一个数进行处理,只需处理两个差分后的数即可;
(2)询问区间和问题:
由性质(2)我们可以计算出数列各项的前缀和数组sum各项的值;那么显然,区间[L,R]的和即为ans=sum[R]-sum[L-1];
例题:
维护两个差分数组,逆序遍历,因为改变的都是之前的操作数
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<set>
#include<map>
#include<iterator>
#include<queue>
#include<vector>
#include<string>
using namespace std;
typedef long long ll;
const int N=1e6+10;
const long long INF=1e18;
const double eps=0.0000001;
const ll mod=1000000007;
struct note
{
int l,r,id,vl;
}point[N];
int f1[N];//维护每个房子中pika的数目,差分
int f2[N];//维护每次操作的重复次数,差分
int d1[N];//具体总个数
int d2[N];//操作总次数
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int t,n,m;
cin>>t;
while(t--)
{
cin>>n>>m;
memset(f1,0,sizeof(f1));
memset(f2,0,sizeof(f2));
memset(d1,0,sizeof(d1));
memset(d2,0,sizeof(d2));
for(int i=1;i<=m;i++)
{
cin>>point[i].id>>point[i].l>>point[i].r;
}
f2[m]=1;
f2[0]=-1;
for(int i=m;i>=1;i--)
{
d2[i]=(f2[i]+d2[i+1])%mod;
if(point[i].id==1)
{
f1[point[i].l]=(f1[point[i].l]+d2[i])%mod;
f1[point[i].r+1]=(f1[point[i].r+1]-d2[i]+mod)%mod;
}
else
{
f2[point[i].r]=(f2[point[i].r]+d2[i])%mod;
f2[point[i].l-1]=(f2[point[i].l-1]-d2[i]+mod)%mod;
}
}
for(int i=1;i<=n;i++)
{
d1[i]=(f1[i]+d1[i-1])%mod;
}
for(int i=1;i<n;i++)
{
cout<<d1[i]<<" ";
}
cout<<d1[n]<<endl;
}
}