2021-11-16每日刷题打卡
AcWing——算法基础
797. 差分 - AcWing题库
输入一个长度为 n 的整数序列。
接下来输入 m 个操作,每个操作包含三个整数 l,r,c,表示将序列中 [l,r ] 之间的每个数加上 c。
请你输出进行完所有操作后的序列。
输入格式
第一行包含两个整数 n 和 m。
第二行包含 n 个整数,表示整数序列。
接下来 m 行,每行包含三个整数 l,r,c 表示一个操作。
输出格式
共一行,包含 n 个整数,表示最终序列。
数据范围
1≤n,m≤100000
1≤l≤r≤n
−1000≤c≤1000
−1000≤整数序列中元素的值≤1000
输入样例:
6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1
输出样例:
3 4 5 3 4 2
昨天我们学到的前缀和用到的是前缀和数组,今天这里我们用到的是逆前缀和数组,也叫差分数组。先介绍一下差分数组是什么:我们知道,前缀和数组的各位数,就是原数组上的前面所有数的和,比如原数组是1 2 3 4 5,那么前缀和数组的第三位就是前三个数的和,即6,这就是前缀和数组。而逆前缀和就说,原数组是这个差分数组的前缀和数组(关系逆转了),比如原数组是1 2 3 4 5,那么差分数组就该是 1 1 1 1 1,这样原数组上的第四位数,就是差分数组的1~4之和,即4。差分数组各位上的运算如下:(a为原数组,b为差分)b[i]=a[i]-a[i-1],不理解的可以想一下:b[1]=a[1]-a[0]、b[2]=a[2]-a[1]、b[3]=a[3]=a[2],这样b1+b2+b2就等于a3了。
然后为什么把区间[l,r]加上c要用差分数组呢,如果我们按正常的情况,就是遍历a中[l,r]位置上的数,然后把c加上去,时间复杂度是n,如果多次重复这种操作容易超时。而差分数组可以化简这一操作,比如正常的数组为1 2 3 4 5,现在你要把[2,4]上的数都加上3,原数组就变成1 5 6 7 5,而差分数组只要把l位置上的数加上3即可,即 1 4 1 1 1 ,因为原数组上的数是差分数组的前缀和,那么只要l上加上c,那么l和l之后的所有数都会加上c了,但有一点注意的是,我们加的数是有区间范围的,比如说上面的例子,我们只想24位置上的数+3,但用拆分数组算后发现第5位数也加上了3变成了8,这不是我们想要的,所以我们要在区间之外的数都-3,即差分数组r+1位置上的数-3即可,这样l位置前的数不会收到+3的影响,lr位置上的数会+3,r之后的数会+3后-3,即不会变化。这样就可以很方便的把原数组l~r区间的数都加上c了,比起对原数组遍历,差分数组只用在l和r+1上两个位置做动作,时间复杂度为O(1)。
#include<iostream>
using namespace std;
const int N=100010;
int n,m;
int a[N],b[N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[i]=a[i]-a[i-1];
}
while(m--)
{
int l,r,c;
cin>>l>>r>>c;
b[l]+=c;
b[r+1]-=c;
}
for(int i=1;i<=n;i++)
{
a[i]=a[i-1]+b[i];
cout<<a[i]<<" ";
}
return 0;
}
798. 差分矩阵 - AcWing题库
输入一个 n 行 m 列的整数矩阵,再输入 q 个操作,每个操作包含五个整数 x1,y1,x2,y2,c,其中 (x1,y1)和 (x2,y2) 表示一个子矩阵的左上角坐标和右下角坐标。
每个操作都要将选中的子矩阵中的每个元素的值加上 cc。
请你将进行完所有操作后的矩阵输出。
输入格式
第一行包含整数 n,m,q。
接下来 n 行,每行包含 m 个整数,表示整数矩阵。
接下来 q 行,每行包含 5 个整数 x1,y1,x2,y2,c 表示一个操作。
输出格式
共 n 行,每行 m 个整数,表示所有操作进行完毕后的最终矩阵。
数据范围
1≤n,m≤1000
1≤q≤100000
1≤x1≤x2≤n
1≤y1≤y2≤m
−1000≤c≤1000
−1000≤矩阵内元素的值≤1000
输入样例:
3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 1
输出样例:
2 3 4 1
4 3 4 1
2 2 2 2
用的也是差分数组,解释起来有点长,感兴趣的可以通过公式+画图来理解
#include<iostream>
using namespace std;
const int N=1010;
int n,m,q;
int a[N][N],b[N][N];
void insert(int x1,int y1,int x2,int y2,int c)
{
b[x1][y1]+=c;
b[x1][y2+1]-=c;
b[x2+1][y1]-=c;
b[x2+1][y2+1]+=c;
}
int main()
{
cin>>n>>m>>q;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
insert(i,j,i,j,a[i][j]);
}
while(q--)
{
int x1,y1,x2,y2,c;
cin>>x1>>y1>>x2>>y2>>c;
insert(x1,y1,x2,y2,c);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
cout<<b[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
799. 最长连续不重复子序列 - AcWing题库
给定一个长度为 n 的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。
输入格式
第一行包含整数 n。
第二行包含 n 个整数(均在 0∼10^5范围内),表示整数序列。
输出格式
共一行,包含一个整数,表示最长的不包含重复的数的连续区间的长度。
数据范围
1≤n≤10^5
输入样例:
5
1 2 2 3 5
输出样例:
3
准备两个数组,a[N],s[N],N开到100010。再用一个数res来存贮最长序列的长度。先把数都存入a数组里,然后用双指针i和j遍历a,初始i和j都为0,i每次遍历都++,当i>=a.size()时结束循环,i每走一格,s[a[i]]++,这里是为了计算i走过的位置是否有重复数,以a[i]为下标,s[a[i]]就为a[i]这个数出现的次数,然后判断,如果s[a[i]]>1,说明i走过的地方有重复数,此时j开始动,当s[a[i]]不大于1时j停下,每次s[a[j]]–,这里是为了找到和当前a[i]相同数字的位置,如果–后s[a[i]]还是大于1,说明还没找到和当前a[i]相同数的位置,j继续++,如果s[a[i]]不大于1了,就说明已经找到相同数的位置,j停止++。每次i++后,对比res和i-j+1的大小(i-j+1代表当前连续子序列的长度),取较大的那个给res赋值。当i走到数组尽头时结束遍历,输出res。
#include<iostream>
using namespace std;
const int N=100010;
int n;
int a[N],s[N];
int main()
{
int res=0;
cin>>n;
for(int i=0;i<n;i++)scanf("%d",&a[i]);
for(int i=0,j=0;i<n;i++)
{
s[a[i]]++;
while(s[a[i]]>1)
{
s[a[j]]--;
j++;
}
res=max(res,i-j+1);
}
cout<<res<<endl;
return 0;
}
800. 数组元素的目标和 - AcWing题库
给定两个升序排序的有序数组 A 和 B,以及一个目标值 x。
数组下标从 0 开始。
请你求出满足 A[i]+B[j]=x 的数对 (i,j)。
数据保证有唯一解。
输入格式
第一行包含三个整数 n,m,x,分别表示 A 的长度,B 的长度以及目标值 x。
第二行包含 n 个整数,表示数组 A。
第三行包含 m 个整数,表示数组 B。
输出格式
共一行,包含两个整数 i 和 j。
数据范围
数组长度不超过 10^5。
同一数组内元素各不相同。
1≤数组元素≤10^9
输入样例:
4 5 6
1 2 4 7
3 4 6 8 9
输出样例:
1 1
双指针就是我亲人啊,我们以前写过一个数组从头到尾的,这次变成两个而已,没区别。一个指针从a数组的开头走,一个指针从b数组的尾部走,加一起大于若x就把b数组的指针往后挪,若小于x就把a数组指针往前挪,相等就停止遍历然后输出。(注意,双指针遍历的这种方式必须要求数组是有序的,不然会错)
#include<iostream>
using namespace std;
const int N=100010;
int n,m,x;
int a[N],b[N],c[N];
int main()
{
cin>>n>>m>>x;
for(int i=0;i<n;i++)scanf("%d",&a[i]);
for(int i=0;i<m;i++)scanf("%d",&b[i]);
int i=0,j=m-1;
while(1)
{
if(a[i]+b[j]>x)j--;
else if(a[i]+b[j]<x)i++;
else break;
}
cout<<i<<" "<<j<<endl;
return 0;
}
2816. 判断子序列 - AcWing题库
给定一个长度为 n 的整数序列 a1,a2,…,an 以及一个长度为 m 的整数序列 b1,b2,…,bm。
请你判断 a 序列是否为 b 序列的子序列。
子序列指序列的一部分项按原有次序排列而得的序列,例如序列 {a1,a3,a5} 是序列 {a1,a2,a3,a4,a5}的一个子序列。
输入格式
第一行包含两个整数 n,m。
第二行包含 n 个整数,表示 a1,a2,…,an
第三行包含 m 个整数,表示 b1,b2,…,bm
输出格式
如果 a 序列是 b 序列的子序列,输出一行 Yes
。
否则,输出 No
。
数据范围
1≤n≤m≤10^5
−109≤ai,bi≤109
输入样例:
3 5
1 3 5
1 2 3 4 5
输出样例:
Yes
双指针真的是我亲人!这两题是我目前唯二没看y总视频写出来的。
准备两个数组a、b,a数组用来存小的,b数组用来存大的,准备两个指针i、j,初始分别指向a、b的开头,然后开始遍历,当a[i]=b[j]上i才++,然后j一直往前走。当遍历结束后判断一下i是否走完了a数组,如果是就输出Yes,反之输出No。
#include<iostream>
using namespace std;
const int N=100010;
int n,m;
int a[N],b[N];
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)scanf("%d",&a[i]);
for(int i=0;i<m;i++)scanf("%d",&b[i]);
int i=0,j=0;
while(j<m&&i<n)
{
if(a[i]==b[j])i++;
j++;
}
if(i==n)cout<<"Yes"<<endl;
else cout<<"No"<<endl;
return 0;
}