定义:
记录当前位置的数与上一位置的数的差值。
举个例子:
可以发现∑ b[j](j = 1 to i)即代表a[i] 的值. (∑ 即代表累加.)
我们可能认为这是前缀和,实际上这并不是真正意义上的前缀和.
前缀和的思想是 根据元素与元素之间的并集关系(和的关系),求出某些元素的和的值.对应的为∑ a[j](j = 1 to i)
而差分的思想与此不同.
差分的思想是 根据元素与元素之间的逻辑关系(大小关系),求出某一位置元素的值.对应的为∑ b[j](j = 1 to i)
用途:
1. 快速处理区间加减操作:O(1)。
2. 询问区间和:O(n)处理O(1)查询。
m次操作,每一次操作,给定l,r,del.将l~r区间的所有数增加del;
最后有q个询问,给你 l,r ,每一次询问求出l~r的区间和。
PS: 先进行m个修改操作,后进行查询操作.
考虑我们差分数组记录的是什么,它记录的是当前位置的数与上一个数的差值.
如果我们在差分数组的 b_x减去del 在b_{y+1}位置处加上del,就能达到整个区间修改的操作.
还是刚开始的栗子.
这样是不是达到了区间修改操作,是不是! "是!,tql!!"
这样我们就能做到O(1)修改啦!
我们再定义两个数组(先不要忘记我们的差分数组为b_i
s_i代表∑ b[j](j = 1 to i) (其实就是代表a[i] qwq
sum_i代表∑ s[j](j = 1 to i) 即代表前缀和. qwq
容易发现的是 sum_r -sum_{l-1}=∑ s[j](j = 1 to i)
什么不理解为什么是l-1?
那我们把式子展开看 qwq
emmmm 差点跑去讲前缀和
所以说我们可以在修改操作完成之后,O(n)的计算我们的sum数组,然后在询问的时候O(1)的输出了.
具体这个题的代码是这样的↓
例题
法一:差分数组区间修改加查询。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int INF = 1000000000;
const int maxn = 100;
ll a[1000001];
ll b[1000001];
ll mod=998244353;
ll qpow(ll a,ll b)
{
ll res=1;
while(b)
{
if(b&1)
{
res=(res%mod*a%mod)%mod;
}
a=a%mod*a%mod;
b>>=1;
}
return res%mod;
}
int main()
{
int t;
int n,m;
cin>>t;
while(t--)
{
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
cin>>n>>m;
int l,r,x;
while(m--)
{
cin>>l>>r>>x;
if(x==2)
{
a[l]++;//差分数组记录2的次数
a[r+1]--;
}
else
{
b[l]++;//记录3的次数
b[r+1]--;
}
}
ll min1=a[1];
ll min2=b[1];
for(int i=2; i<=n; i++)
{
a[i]+=a[i-1];//维护区间最小值
b[i]+=b[i-1];
min1=min(a[i],min1);
min2=min(b[i],min2);
}
min1=qpow(2,min1)%mod;
min2=qpow(3,min2)%mod;
ll res=(min1*min2)%mod;
cout<<res<<endl;
}
return 0;
}
法二线段树: