Basic Thought / Data Structure: 差分 Difference
Intro:
作为查询界的 \(O(1)\) 王者——前缀和的亲兄弟,差分,他可是修改界的 \(O(1)\) 王者
Prerequisite knowledge:
Function: 仅单次询问的区间修改
模板题:洛谷P2367 语文成绩
先想一想朴素算法怎么做吧
对于输入的每一组 \((x,y)\) ,遍历序列 \(a_{x..y}\) ,每一项加上 \(z\) ,代码如下
while(p--)for(int i(x);i<=y;++i)a[i]+=z;
Time complexity: \(O(np)\)
Memory complexity: \(O(n)\)
这个时间复杂度明显不行,只要修改长度大了,修改时间就会变长
发现 查询只有一次
也就是说如果可以把数组变一下,使得修改 \(O(1)\) ,而查询 \(O(n)\) ,就可以接受了
我们构造一个数组 \(d_{1..n}\) ,其中
\(d_i=a_i-a_{i-1}, 1\leqslant i\leqslant n\)
也就是说 \(d\) 的每一项都是原来 \(a\) 的同位项与 \(a\) 的前一项的差
反过来看的话,\(a_i=a_{i-1}+d_i\)(是不是有点眼熟呢)
没错! \(a_i=\sum_{j=1}^id_j\) ,是前缀和!
考虑一下对 \(d_i\) 加上 \(v\) 对 \(a\) 会有什么效果
对于 \(a_{1..i-1}\) ,没效果
对于 \(a_{i..n}\) ,每一个值都加上了 \(v\) (认真思考下为什么)
所以对于每一次区间修改 \((l,r,v)\) (表示对 \(a_{l..r}\) 每一个元素加上 \(v\) ),对 \(d\) 来说,只不过是把 \(d_l\) 加上 \(v\) , \(d_{r+1}\) 减去 \(v\) 而已
这就是\(d_{1..n}\),差分数组
最后如果想要把\(d\)数组变回\(a\)数组,只要对\(d\)构造前缀和数组即可(这就是为什么我说差分是前缀和的逆运算了)
Code:
构造 \(d_{1..n}\) :
for(int i(n);i>=1;--i)d[i]=a[i]-a[i-1];
P.s: 其实顺序没有关系,但是如果原址构造必须倒序
修改
d[l]+=v,d[r+1]-=v;
更新原数组
for(int i(1);i<=n;++i)a[i]=a[i-1]+d[i]
Time complexity: \(O(n)\) 预处理 \(O(1)\) 修改 \(O(n)\) 查询(以后这三项简称为 \(O(n)-O(1)-O(n)\))
Memory complexity: \(O(n)\)
P.s 同样的,可以直接将原数组变成差分数组,则不需要额外空间,代码如下(必须倒序)
for(int i(n);i>=2;--i)a[i]-=a[i-1];
于是本题的答案就出来啦,具体见代码(记得开long long)
//This program is written by Brian Peng.
#pragma GCC optimize("Ofast","inline","-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define Rd(a) (a=read())
#define Gc(a) (a=getchar())
#define Pc(a) putchar(a)
inline int read(){
register int x;register char c(getchar());register bool k;
while(!isdigit(c)&&c^'-')if(Gc(c)==EOF)exit(0);
if(c^'-')k=1,x=c&15;else k=x=0;
while(isdigit(Gc(c)))x=(x<<1)+(x<<3)+(c&15);
return k?x:-x;
}
void wr(register int a){
if(a<0)Pc('-'),a=-a;
if(a<=9)Pc(a|'0');
else wr(a/10),Pc((a%10)|'0');
}
signed const INF(0x3f3f3f3f),NINF(0xc3c3c3c3);
long long const LINF(0x3f3f3f3f3f3f3f3fLL),LNINF(0xc3c3c3c3c3c3c3c3LL);
#define Ps Pc(' ')
#define Pe Pc('\n')
#define Frn0(i,a,b) for(register int i(a);i<(b);++i)
#define Frn1(i,a,b) for(register int i(a);i<=(b);++i)
#define Frn_(i,a,b) for(register int i(a);i>=(b);--i)
#define Mst(a,b) memset(a,b,sizeof(a))
#define File(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
#define N (5000010)
int n,p,a[N],x,y,z,ans(LINF);
signed main(){
Rd(n),Rd(p);
Frn1(i,1,n)Rd(a[i]);
Frn_(i,n,1)a[i]-=a[i-1];
while(p--)Rd(x),Rd(y),a[x]+=Rd(z),a[y+1]-=z;
Frn1(i,1,n)ans=min(ans,a[i]+=a[i-1]);
wr(ans),exit(0);
}
Example:
(这不是暴力就可以了吗)
当然正解就是暴力,所以假设 \(n,m,r\leqslant 1000\) ,再看看这道题
这道题就是典型的区间修改(将可以覆盖的地方 \(+1\) )与单次查询(最后问 \(>0\) 的有几个)
于是以行为单位差分,每一次修改相当于对每一行的元素做区间修改
但是每一行被修改的位置和长度互不相同,具体取决于半径 \(r\) 和行号 \(i\) 到圆心所在行号 \(y\) 的距离 \(|i-y|\)
假设存在 \(e_{0..r}\) 数组, \(e_i\) 表示斜边为 \(r\) ,对边为 \(i\) 的直角三角形的邻边长度的整数部分,即 \(e_i=\lfloor\sqrt{r^2-i^2}\rfloor\)
那么对于第 \(i\) 行,如果圆心是 \((x,y)\) ,半径为 \(r\) ,覆盖的区间就是 \([max(1,x-e_{|i-y|}),min(n,x+e_{|i-y|})]\) (可别忘了处理边界)
最后是如何计算 \(e\) 数组
(当然可以直接使用sqrt())
但是注意到\(e\)数组具有不上升单调性,可以使用一种类似递推的方法做,保证计算\(O(n)\)(虽然说其实完全没有必要啦)
具体做法就请大家自己想啦 (其实是本蒟蒻太懒了) ,上代码
//This program is written by Brian Peng.
#pragma GCC optimize("Ofast","inline","-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
using namespace std;
#define Rd(a) (a=read())
#define Gc(a) (a=getchar())
#define Pc(a) putchar(a)
inline int read(){
register int x;register char c(getchar());register bool k;
while(!isdigit(c)&&c^'-')if(Gc(c)==EOF)exit(0);
if(c^'-')k=1,x=c&15;else k=x=0;
while(isdigit(Gc(c)))x=(x<<1)+(x<<3)+(c&15);
return k?x:-x;
}
void wr(register int a){
if(a<0)Pc('-'),a=-a;
if(a<=9)Pc(a|'0');
else wr(a/10),Pc((a%10)|'0');
}
signed const INF(0x3f3f3f3f),NINF(0xc3c3c3c3);
long long const LINF(0x3f3f3f3f3f3f3f3fLL),LNINF(0xc3c3c3c3c3c3c3c3LL);
#define Ps Pc(' ')
#define Pe Pc('\n')
#define Frn0(i,a,b) for(register int i(a);i<(b);++i)
#define Frn1(i,a,b) for(register int i(a);i<=(b);++i)
#define Frn_(i,a,b) for(register int i(a);i>=(b);--i)
#define Mst(a,b) memset(a,b,sizeof(a))
#define File(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
#define N (110)
int n,m,r,a[N][N],e[N],x,y,ans;
signed main(){
Rd(n),Rd(m),*e=Rd(r);
Frn1(i,1,r){e[i]=e[i-1];while(e[i]*e[i]+i*i>r*r)--e[i];}
while(m--){
Rd(x),Rd(y);
Frn1(i,max(1,y-r),min(n,y+r)){
++a[max(1,x-e[abs(i-y)])][i];
--a[min(n,x+e[abs(i-y)])+1][i];
}
}
Frn1(i,1,n)Frn1(j,1,n)if(a[i][j]+=a[i-1][j])++ans;
wr(ans),exit(0);
}
(个人认为如果\(n,m,r\leqslant 1000\),这道题至少上绿)
到此为止差分的所有基本操作都讲完啦!
Conclusion & Extension:
差分在区间修改和单词查询问题上,有着 \(O(n)-O(1)-O(n)\) 的优秀复杂度
它的基本思想就是用一个数字的变化表示一段区间的整体变化,达到优化的目的
其实,大家有木有感觉到这个差分有点像离散版的微分(把 \(\Delta x\) 设为 \(1\) )?
当差分遇上数据结构,一段美妙的旅程又将开始:树状数组(嘻嘻嘻又是我)
到此为止本篇文章就圆满结束啦,请各位奆佬们多多指教和支持,THX!
Basic Thought / Data Structure: 差分 Difference的更多相关文章
- Basic Thought / Data Structure: 前缀和 Prefix Sum
Intro: 在OI中,前缀和是一种泛用性很高的数据结构,也是非常重要的优化思想 Function: 求静态区间和 模板题:输入序列\(a_{1..n}\),对于每一个输入的二元组\((l,r)\), ...
- hdu-5929 Basic Data Structure(双端队列+模拟)
题目链接: Basic Data Structure Time Limit: 7000/3500 MS (Java/Others) Memory Limit: 65536/65536 K (Ja ...
- HDU 5929 Basic Data Structure 模拟
Basic Data Structure Time Limit: 7000/3500 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Oth ...
- HDU 5929 Basic Data Structure 【模拟】 (2016CCPC东北地区大学生程序设计竞赛)
Basic Data Structure Time Limit: 7000/3500 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Oth ...
- Basic Data Structure
Basic Data Structure Time Limit: 7000/3500 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Oth ...
- Basic Data Structure HDU - 5929 (这个模拟我要报警了)
Mr. Frog learned a basic data structure recently, which is called stack.There are some basic operati ...
- Finger Trees: A Simple General-purpose Data Structure
http://staff.city.ac.uk/~ross/papers/FingerTree.html Summary We present 2-3 finger trees, a function ...
- hdu 4217 Data Structure? 树状数组求第K小
Data Structure? Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) ...
- Python: tree data structure
# 树结构 from pythonds.basic.stack import Stack #pip install pythonds from pythonds.trees.binaryTree im ...
随机推荐
- ABP在MultipleDbContext也就是多库的场景下发布后异常“Could not find content root folder”问题处理
ABP多库支持 ABP支持多库的方案在abp的案例中aspnetboilerplate-samples中给了现成的demo,其中MultipleDbContextDemo是EF的相关针对dotnet的 ...
- UVW平台运动控制算法以及matlab仿真
UVW平台运动控制算法以及matlab仿真 最近公司同事因为对某视觉对位平台的运动控制算法有疑问,所以来请教我.由于我也是第一次接触到UVW自动对位平台(也可以叫XXY自动对位平台),于是找了一些 ...
- Java8 LocalDate计算两个日期的间隔天数
Java8新增了java.time包,提供了很多新封装好的类,使我们可以摆脱原先使用java.util.Time以及java.util.Calendar带来的复杂. 其中LocalDate正是本文中使 ...
- JavaScript 构造树形结构的一种高效算法
引言 我们经常会碰到树形数据结构,比如组织层级.省市县或者动植物分类等等数据.下面是一个树形结构的例子: 在实际应用中,比较常见的做法是将这些信息存储为下面的结构,特别是当存在1对多的父/子节点关系时 ...
- $Poj1821\ Fence\ $单调队列优化$DP$
Poj Acwing Description 有N块木板等待被M个工匠粉刷,每块木板至多被刷一次.第i个工匠要么不粉刷,要么粉刷包含木块Si的,长度不超过Li的连续的一段木板,每粉刷一块可以得到P ...
- 开源项目SMSS开发指南
SMSS是一个由我个人发起的开源项目,目的是建立一套轻量化,高可用,高安全和方便扩展的业务支撑框架.SMSS面向TCP/IP层开发,适合扩展上层业务接口.数据结构传输序列化通过Protobuf实现.传 ...
- 异常记录——bat批处理闪退
bat批处理闪退 bat描述 我的博客每次更新需要跑多个命令 clean(清除旧文)+g(生成新文)+d(部署到服务器),作为一个懒惰的程序员,自然要写一个bat一键完成 E: cd blog hex ...
- 边框,元素居中,盒子模型,margin,display,overflow,textarea,float,浮动停止条件,清除浮动影响,margin-top的bug,清除默认样式
边框 solid实线 dotted虚线 dashed点线 盒子在页面中实际的宽高都是5部分组成 宽=borderleft+paddingleft+width+paddingright+borderri ...
- 微信小程序开发笔记(二)
一.前言 继承上一篇所说的,有了对微信小程序的基础概念后,这边将会示范动手做一个小程序,在动手的过程中我们可以更快的熟悉小程序里面的架构和开发流程. 二.小程序的设计 这次要做的是一个猜数字的程序,程 ...
- 网络爬虫简单介绍(python)
一.简介 爬虫就是利用代码大量的将网页前端代码下载下来使用的一种程序,一般来说常见的目的为下: 1.商业分析使用:很多大数据公司都会从利用爬虫来进行数据分析与处理,比如说要了解广州当地二手房的均价走势 ...