Loading

差分&前缀和 笔记

我们在打 OI 的时候经常遇见维护一个区间或者是对一个区间进行批量操作,如果这个区间我们需要维护的区间很大或者操作次数很多,暴力往往会到 \(O(nq)\),容易 TLE。

因此我们引入前缀和这个概念。

假设我们拥有一个 \(A\) 数组

\[A=[1,2,3,4,5,6] \]

这时候我们构建一个前缀和数组,使用下面代码。

for(int i=0;i<n;i++){
    if(i==0){ //如果是第一项我们特批
        p[i]=a[i];
    }else{
        p[i]=p[i-1]+a[i]; //如果不是我们就表达式
    }
}

表达式也就是

值得注意:构建的数组的第一项还是原数组的第一项

前缀和定义(\(0\)-index):

\[p_i=\sum_{k=0}^{i} a_k \qquad (0\le i\le n-1) \]

前缀和递推(令 \(p_{-1}=0\)):

\[p_i=p_{i-1}+a_i \qquad (0\le i\le n-1) \]

构建出来这个前缀和数组之后,我们每次查询前缀的时间复杂度都是 \(\Theta(1)\) 的,而我们构建一个前缀和数组所需要的时间复杂度是 \(\Theta(n)\)。构建是 \(\Theta(n)\)(也是 \(O(n)\)),单次查询是 \(\Theta(1)\)(也是 \(O(1)\))。

我们有了前置知识前缀和之后我们就可以引入差分的概念了。

本质上来讲差分就是和前缀和互逆的操作,类似于平方和开根,跟之前一样。

我们构建一个差分数组,使用以下代码。

vector<int> d(n);
for(int i=0;i<n;i++){
    if(i==0){
        d[i]=a[i];
    }else{
        d[i]=a[i]-a[i-1];
    }
}

表达式也就是

值得注意:构建的数组的第一项还是原数组的第一项

差分定义(\(0\)-index):

\[d_0=a_0,\qquad d_i=a_i-a_{i-1}\qquad (1\le i\le n-1) \]

差分还原(对 \(d\) 做前缀和得到原数组):

\[a_i=\sum_{k=0}^{i} d_k \qquad (0\le i\le n-1) \]

区间加(把区间 \([l,r]\) 所有元素都加 \(c\))的目标写法:

\[a'*i = a_i + c\cdot \mathbf{1}*{,l\le i\le r} \]

差分数组上的等价更新:

\[d'_l = d_l + c \]

\[d'*{r+1}=d*{r+1}-c \qquad (\text{若 } r+1<n) \]

\[d'_i=d_i \qquad (\text{其余 } i) \]

其中 \(\mathbf{1}_{,l\le i\le r}\) 是指示函数:条件成立取 1,否则取 0。

如果你构建好了差分数组对差分数组进行一次前缀和你就惊奇的发现!竟然变成了原始数组!有了这个概念我们就可以讲怎么对一个区间进行统一操作了。

给你一个长度为 \(n\) 的数组,对数组的 \(l\)\(r\) 进行修改里面所有元素加上 \(c\)

我们分析一下这刚好可以使用前缀和+差分!

下面的代码先对原始数组进行一次差分然后对 \(l\) 的位置加上 \(c\) 由于我们想在 \(r\) 的位置停止那么我们在 \(r+1\) 的位置减去 \(c\) 就好了。

值得注意的是:如果你在做区间操作时不特判可能会 RE,具体的 check 如下:

if(r+1<n){
    diff[r+1] -= c;
}

下面是练习题目:

P2367 语文成绩

对于语文成绩的我的解法, \(O(n)\)

aclink

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define uint unsigned int
#define i128 __int128
#define ld long double
#define fir first
#define sec second
#define pii pair<int,int>
#define pll pair<ll,ll>
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define lowbit(x) (x&-x)
using namespace std;
const int MOD=998244353;
const int MOD1=1e9+7;
//char ibuf[1<<25],*p1=buf,*p2=buf;
mt19937 mrand(random_device{}());
int rnd(int x){ return mrand() % x;}
ll qpow(ll a,ll b){ll res=1;while(b){if(b&1)res=res*a%MOD;a=a*a%MOD,b>>=1;}return res;}
ll gcd(ll a,ll b){ return b?gcd(b,a%b):a;}  
//C++ 17 -O2
//By MaZhaoze
int main(){
	//O(n)
	int n,p;
	cin>>n>>p;
	vector<int> a(n);
	for(int i=0;i<n;i++) cin>>a[i];
	vector<int> diff(n);
	for(int i=0;i<n;i++){
		if(i==0){
			diff[i]=a[i];
		}else{
			diff[i]=a[i]-a[i-1];
		}
	}
	while(p--){
		int x,y,z;
		cin>>x>>y>>z;
		x--;
		y--;
		diff[x]+=z;
		//diff[y+1]-=z;
		if(y+1<n){
			diff[y+1] -= z;
		}
	}
	vector<int> pf(n);
	int ans=diff[0];
	for(int i=0;i<n;i++){
		if(i==0){
			pf[i]=diff[i];
		}else{
			pf[i]=pf[i-1]+diff[i];
			ans=min(ans,pf[i]);
		}
	}
	cout<<ans;
	return 0;
}
posted @ 2026-01-08 23:42  MagnusSM2  阅读(21)  评论(0)    收藏  举报