理论知识:
总的来说:差分简单来讲就是前缀和的逆过程
定义:
假设有一个递增序列s[N],有一个序列a[N]满足{ a1=s1 , a2=s2-s1 , a3=s3-s2 ..... an=sn-sn- 1 }
那么a序列就称为s序列的差分序列;
最常用性质:
有一个数组a[n],其对应的差分数组b[n]为b1=a1,b2=a2-a1,b3=a3-a2......bn=an-an-1;
如果要对原数组的[l,r]区间上的数同时增加一个大小为c的数,暴力给每个数加上c,时间复杂度为O(n),在其对应的差分数组上
进行inerst操作则可以实现插入时间复杂度为O(1).(主要作用就是降低时间复杂度,可以看作是对暴力算法的优化,即剪枝)
类似于连锁效应,底层的数组b发生改变,作为b数组前缀和的a数组会发生一连串的变化,使得时间复杂度缩小。
void inerst(int l,int r,int c)
{
b[l]+=c;
b[r+1]-=c;
}
(对于如何在已知s[]序列时,利用inerst()函数巧妙得求出对应的差分数组,可移步至查看→链接);
实践例题:
今日份刷题:k倍区间和,改变数组元素,差分,差分矩阵,增减数列;
如果对“k倍区间“感兴趣的见→链接;
下面分享例题”改变数组元素“
题目
给定一个空数组 V 和一个整数数组 a1,a2,…,an。
现在要对数组 V 进行 n 次操作。
第 i 次操作的具体流程如下:
从数组 V 尾部插入整数 0。
将位于数组 V 末尾的 ai 个元素都变为 1(已经是 1 的不予理会)。
注意:
ai 可能为 0,即不做任何改变。
ai 可能大于目前数组 V 所包含的元素个数,此时视为将数组内所有元素变为 1。
请你输出所有操作完成后的数组 V。
输入格式
第一行包含整数 T,表示共有 T 组测试数据。
每组数据第一行包含整数 n。
第二行包含 n 个整数 a1,a2,…,an。
输出格式
每组数据输出一行结果,表示所有操作完成后的数组 V,数组内元素之间用空格隔开。
数据范围
1≤T≤20000,
1≤n≤2×1e5,
0≤ai≤n,
保证一个测试点内所有 n 的和不超过 2×1e5。
输入样例:
3
6
0 3 0 0 1 3
10
0 0 0 1 0 5 0 0 0 2
3
0 0 0
输出样例:
1 1 0 1 1 1
0 1 1 1 1 1 0 0 1 1
0 0 0
代码+题解:
有助于理解的关键代码都会加上注释
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=2e5+10;//观察题目会发现一开始的数据就必须会有两层循环,时间复杂度达到了4e9;
int q[N],v[N];
void Inerst(int l,int r){//inerst函数对差分数组处理
v[l]+=1;
v[r+1]-=1;
return;
}
int main(){
int T;
cin>>T;
while(T--){
int n;
cin>>n;
memset(q,0,n*4);//这里尽量缩小数据范围,不要使用N,时间会有几倍差.这里简单讲一下memset的用法:第 一个参数是目标起始地址,第二个参数是需要设定的值,第三个是总的字节大小;memset 是按字节处理的,所以第三个参数可以使用sizeof()作为参数;其头文件为#include <c string>
memset(v,0,n*4);
for(int i=1;i<=n;i++)
scanf("%d",&q[i]);//储存每组数据
for(int i=1;i<=n;i++){
if(!q[i]) continue;//特判,是q[i]==0就跳过
if(i<=q[i]) Inerst(1,i);//这里最为关键,不需要考虑考虑区间是否重复加1
else Inerst(i-q[i]+1,i);
}
for(int i=1;i<=n;i++) v[i]+=v[i-1];//已知差分数组,求原数组,直接求前缀和
for(int i=1;i<=n;i++){//代码30~31可以使用cout<<!!v[i]<<' ';来代替(双非)
if(v[i]) cout<<1<<' ';
else cout<<0<<' ';
};
cout<<endl;
}
return 0;
}
总结:
1.值得高兴的是在解”改变数组元素“时学会了使用数据debug,成功AC;
2.解题还是得多注意数据范围;
如有哪里有疑惑的地方还请评论区留言,如果有错误的地方希望多多指教~~~///(^v^)\\\~~~