差分操作正确性证明

差分操作正确性证明

本文是作者因题目写差分写挂了后随手总结的。

定义

对于一个长度为 n n n 的数组 a a a,定义其差分数组为 p p p,且 ∀ 1 ≤ i ≤ n , p i = a i − a i − 1 ( a 0 = 0 ) \forall 1\le i\le n,p_i=a_i-a_{i-1}(a_0=0) ∀1≤i≤n,pi=ai−ai−1(a0=0)。

转化回原数列

给些式子就懂了。

根据定义:
p 1 + p 2 + p 3 + ⋯ + p k = a 1 + ( a 2 − a 1 ) + ( a 3 − a 2 ) + ⋯ + ( a k − a k − 1 ) = a k p_1+p_2+p_3+\cdots +p_k\\ =a_1+(a_2-a_1)+(a_3-a_2)+\cdots +(a_k-a_{k-1})\\ =a_k p1+p2+p3+⋯+pk=a1+(a2−a1)+(a3−a2)+⋯+(ak−ak−1)=ak

所以, a k = ∑ i = 1 k p i a_k=\sum_{i=1}^k p_i ak=∑i=1kpi。
a k = ∑ i = 1 k p i a k = ∑ i = 1 k − 1 p i + p k a k = a k − 1 + p k a_k=\sum_{i=1}^k p_i\\ a_k=\sum_{i=1}^{k-1}p_i+p_k\\ a_k=a_{k-1}+p_k ak=i=1∑kpiak=i=1∑k−1pi+pkak=ak−1+pk

另一种:
p x = a x − a x − 1 a x = a x − 1 + p x p_x=a_x-a_{x-1}\\ a_x=a_{x-1}+p_x px=ax−ax−1ax=ax−1+px

所以只要把 p p p 数组当作新的原数组,再将这个数组做个前缀和就 OK 了。

区间加法

假设需要将原数列的 l , r l,r l,r 全部加上 v v v。那么暴力是 O ( n ) O(n) O(n) 的,考虑用差分优化。

公式: p l ← p l + v , p r + 1 ← p r + 1 − v p_l\gets p_l+v,p_{r+1}\gets p_{r+1}-v pl←pl+v,pr+1←pr+1−v。

证明:

显然 ∀ 1 ≤ i ≤ l − 1 \forall 1\le i\le l-1 ∀1≤i≤l−1, p i p_i pi 是不变的。那么考虑转化的本质。

我们设 s i s_i si 表示修改后的数组。即 s i = s i − 1 + p i s_i=s_{i-1}+p_i si=si−1+pi。

∀ l ≤ i ≤ r s i = ∑ j = 1 i p j s i = ∑ j = 1 i − 1 p j + p i s i = s i − 1 + p j \forall l\le i\le r\\ s_i=\sum_{j=1}^{i}p_j\\ s_i=\sum_{j=1}^{i-1}p_j+p_i\\ s_i=s_{i-1}+p_j ∀l≤i≤rsi=j=1∑ipjsi=j=1∑i−1pj+pisi=si−1+pj

那么在 l , r l,r l,r 区间里的数,在前缀和时就会被 s l s_l sl 加上 v v v。而 r + 1 , n r+1,n r+1,n 里的数(不动的),则在 s r + 1 s_{r+1} sr+1 时减回来了,故值不变。

差分大概也就用到这些,再复杂点的就用线段树吧。

例题

给道例题:NOIP 2012 提高组 借教室

代码:(差分+二分,时间复杂度 O ( n log ⁡ m ) O(n\log m) O(nlogm))

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ljl;
const int N=1e6+5;
int n,m;
ljl a[N],tp[N],sum[N],p[N];
struct R{
	int s,t;
	ljl d;
}r[N];
void addlr(int l,int r,int v)
{
	p[l]+=v;p[r+1]-=v;
	return;
}
bool check(int x)//check [1,x]
{
	for(int i=1;i<=n;++i)p[i]=tp[i];
	for(int i=1;i<=x;++i)addlr(r[i].s,r[i].t,-r[i].d);
	memset(sum,0,sizeof(sum));
	for(int i=1;i<=n;++i)
		sum[i]=p[i]+sum[i-1];
	for(int i=1;i<=n;++i)if(sum[i]<0)return 0;
	return 1;
}
int main(){
	ios::sync_with_stdio(0);
	cin>>n>>m;
	for(int i=1;i<=n;++i)cin>>a[i];
	for(int i=1;i<=m;++i)
		cin>>r[i].d>>r[i].s>>r[i].t;
	for(int i=1;i<=n;++i)
		tp[i]=a[i]-a[i-1];
	int l=0,r=m,mid=0;
	while(l<r)
	{
		mid=(l+r+1)>>1;
//		cout<<l<<' '<<r<<' '<<mid<<' '<<check(mid)<<'\n';
		if(check(mid))l=mid;
		else r=mid-1;
	}
	if(l==m){cout<<"0\n";return 0;}
	cout<<"-1\n"<<l+1<<'\n';
	return 0;
}
相关推荐
caimouse2 小时前
reactos编码规范
c语言·开发语言
小雨下雨的雨3 小时前
井字棋AI机器人实现详解 - Minimax算法实战-鸿蒙PC Electron框架完成
前端·人工智能·算法·华为·electron·鸿蒙
xieliyu.6 小时前
Java算法精讲:双指针(三)
java·开发语言·算法
love530love6 小时前
LiveTalking 数字人项目 Windows 部署完全指南(EPGF 架构)
人工智能·windows·python·架构·livetalking·epgf
遇事不決洛必達6 小时前
【Python基础】GIL 锁是什么及其对爬虫的影响
爬虫·python·线程·进程·gil锁
明夜之约6 小时前
Spring Boot 自动装配源码
java·spring boot·后端
Leaton Lee6 小时前
Spring Boot分层架构详解:从Controller到Service再到Mapper的完整流程
java·spring boot·后端·架构
Jinkxs6 小时前
Resilience4j- 与 Spring Boot 快速集成:自动配置与基础注解使用
java·spring boot·后端
辣机小司6 小时前
【踩坑记录:Spring Boot 配置文件读取值不一致?警惕 YAML 的“八进制陷阱”与 SnakeYAML 版本之谜】
java·spring boot·后端·yaml·踩坑记录
一条小锦吕*6 小时前
基于Spring Boot + 数据可视化 + 协同过滤算法的推荐系统设计与实现(源码+论文+部署全讲解)
spring boot·算法·信息可视化