蓝桥前一周刷题 acwing&&new_oj
前缀和
一维前缀和
输入一个长度为 n的整数序列。
接下来再输入 m个询问,每个询问输入一对 l,r。对于每个询问,输出原序列中从第 l个数到第 r个数的和。
输入格式
第一行包含两个整数 n和 m。
第二行包含 n个整数,表示整数数列。接下来 m行,每行包含两个整数 l和 r,表示一个询问的区间范围。
输出格式
共 m行,每行输出一个询问的结果。
数据范围
1≤l≤r≤n
1≤n,m≤100000
−1000≤数列中元素的值≤1000
输入样例:
5 3
2 1 3 6 4
1 2
1 3
2 4
输出样例:
3
6
10
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n,m,a[N],s[N];
int L,R;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)scanf("%d",&a[i]),s[i]=s[i-1]+a[i];
while(m--){
scanf("%d%d",&L,&R);
printf("%d\n",s[R]-s[L-1]);
}
return 0;
}
二维前缀和
输入一个 n行 m 列的整数矩阵,再输入 q个询问,每个询问包含四个整数 x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。
对于每个询问输出子矩阵中所有数的和。
输入格式
第一行包含三个整数 n,m,q。
接下来 n行,每行包含 m个整数,表示整数矩阵。
接下来 q行,每行包含四个整数 x1,y1,x2,y2,表示一组询问。
输出格式
共 q行,每行输出一个询问的结果。
数据范围
1≤n,m≤1000,
1≤q≤200000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
−1000≤矩阵内元素的值≤1000
输入样例:
3 4 3
1 7 2 4
3 6 2 8
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4
输出样例:
17
27
21
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e3+10;
int n,m,q,a[N][N],s[N][N];
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
scanf("%d",&a[i][j]),
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
while(q--){
int x1,x2,y1,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
printf("%d\n",s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]);
}
return 0;
}
截断数组
给定一个长度为 n 的数组 a1,a2,…,an。
现在,要将该数组从中间截断,得到三个非空子数组。
要求,三个子数组内各元素之和都相等。
请问,共有多少种不同的截断方法?
输入格式
第一行包含整数 n。
第二行包含 n个整数 a1,a2,…,an。
输出格式
输出一个整数,表示截断方法数量。
数据范围
前六个测试点满足 1≤n≤10。
所有测试点满足 1≤n≤105,−10000≤ai≤10000。
输入样例1:
4
1 2 3 3
输出样例1:
1
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
using namespace std;
typedef long long LL;
const int N=1e5+10;
int n,a[N],s[N];
unordered_map<int,int>h;
LL res;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",&a[i]),s[i]=s[i-1]+a[i];
for(int i=1;i<n;++i){
if(i>1&&s[i]*3==s[n]*2)res+=h[s[i]/2];
++h[s[i]];
}
printf("%lld\n",res);
return 0;
}
K倍区间
给定一个长度为 N的数列,A1,A2,…AN,如果其中一段连续的子序列 Ai,Ai+1,…Aj之和是 K的倍数,我们就称这个区间 [i,j]是 K倍区间。
你能求出数列中总共有多少个 K倍区间吗?
输入格式
第一行包含两个整数 N和 K。以下 N行每行包含一个整数 Ai。
输出格式
输出一个整数,代表 K倍区间的数目。
数据范围
1≤N,K≤100000,1≤Ai≤100000
输入样例:
5 2
1
2
3
4
5
输出样例:
6
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=1e5+10;
int n,k,a[N],s[N];
int h[N];
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;++i)scanf("%d",&a[i]);
LL res=0;
h[0]=1;
for(int i=1;i<=n;++i){
s[i]=(s[i-1]+a[i])%k;
res+=h[s[i]];
++h[s[i]];
}
printf("%lld\n",res);
return 0;
}
激光炸弹
地图上有 N个目标,用整数 Xi,Yi表示目标在地图上的位置,每个目标都有一个价值 Wi。
注意:不同目标可能在同一位置。
现在有一种新型的激光炸弹,可以摧毁一个包含 R×R个位置的正方形内的所有目标。
激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆炸范围,即那个正方形的边必须x,y轴平行。
求一颗炸弹最多能炸掉地图上总价值为多少的目标。
输入格式
第一行输入正整数 N和 R,分别代表地图上的目标数目和正方形包含的横纵位置数量,数据用空格隔开。
接下来 N行,每行输入一组数据,每组数据包括三个整数 Xi,Yi,Wi,分别代表目标的 x坐标,y坐标和价值,数据用空格隔开。
*输出格式
输出一个正整数,代表一颗炸弹最多能炸掉地图上目标的总价值数目。
数据范围
0≤R≤109
0<N≤10000,
0≤Xi,Yi≤5000
0≤Wi≤1000
输入样例:
2 1
0 0 1
1 1 1
输出样例:
1
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=5e3+10;
int n,r,x,y,w;
int a[N][N];
int main(){
scanf("%d%d",&n,&r);
while(n--){
scanf("%d%d%d",&x,&y,&w);
a[x+1][y+1]+=w;
}
for(int i=1;i<N;++i)
for(int j=1;j<N;++j)
a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
int ans=0;
for(int i=1;i<N;++i)
for(int j=1;j<N;++j){
if(i>=r&&j>=r)ans=max(ans,a[i][j]-a[i-r][j]-a[i][j-r]+a[i-r][j-r]);
else if(i>=r)ans=max(ans,a[i][j]-a[i-r][j]);
else if(j>=r)ans=max(ans,a[i][j]-a[i][j-r]);
else ans=max(ans,a[i][j]);
}
printf("%d\n",ans);
return 0;
}
差分
一维差分
输入一个长度为 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
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n,m,a[N];
int b[N];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)scanf("%d",&a[i]);
while(m--){
int l,r,c;
scanf("%d%d%d",&l,&r,&c);
b[l]+=c;
b[r+1]-=c;
}
for(int i=1;i<=n;++i)b[i]=b[i-1]+b[i];
for(int i=1;i<=n;++i)a[i]+=b[i];
for(int i=1;i<=n;++i){
printf("%d ",a[i]);
}
return 0;
}
二维差分
输入一个 n行 m列的整数矩阵,再输入 q个操作,每个操作包含五个整数 x1,y1,x2,y2,c,其中 (x1,y1) 和 (x2,y2)表示一个子矩阵的左上角坐标和右下角坐标。每个操作都要将选中的子矩阵中的每个元素的值加上 c。
请你将进行完所有操作后的矩阵输出。
输入格式
第一行包含整数 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<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e3+10;
int n,m,q,a[N][N];
int b[N][N];
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
scanf("%d",&a[i][j]);
while(q--){
int x1,x2,y1,y2,c;
scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&c);
b[x1][y1]+=c;
b[x2+1][y1]-=c;
b[x1][y2+1]-=c;
b[x2+1][y2+1]+=c;
}
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
a[i][j]+=b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
printf("%d ",a[i][j]);
}
puts("");
}
return 0;
}
改变数组元素
给定一个空数组 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×105,0≤ai≤n,保证一个测试点内所有 n的和不超过 2×10^5。
输入样例:
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<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=2e5+10;
int n,a[N];
int b[N];
void solve(){
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",&a[i]),b[i]=0;
for(int i=1;i<=n;++i){
b[max(1,i-a[i]+1)]+=1;
b[i+1]-=1;
}
for(int i=1;i<=n;++i)b[i]=b[i-1]+b[i];
for(int i=1;i<=n;++i){
if(b[i]>0)b[i]=1;
printf("%d ",b[i]);
}
puts("");
}
int main(){
int t;
scanf("%d",&t);
while(t--){
solve();
}
return 0;
}
二分
数的范围
给定一个按照升序排列的长度为 n的整数数组,以及 q 个查询。
对每个查询,返回一个元素 k的起始位置和终止位置(位置从 0开始计数)。
如果数组中不存在该元素,则返回 -1 -1。
输入格式
第一行包含整数 n和 q,表示数组长度和询问个数。
第二行包含 n个整数(均在 1∼10000范围内),表示完整数组。
接下来 q行,每行包含一个整数 k,表示一个询问元素。
输出格式
共 q行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回 -1 -1。
数据范围
1≤n≤100000
1≤q≤10000
1≤k≤10000
输入样例:
6 3
1 2 2 3 3 4
3
4
5
输出样例:
3 4
5 5
-1 -1
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n,m,a[N];
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<n;++i)scanf("%d",&a[i]);
while(m--){
int x;
scanf("%d",&x);
int l=lower_bound(a,a+n,x)-a;
int r=upper_bound(a,a+n,x)-a;
if(a[l]!=x)puts("-1 -1");
else printf("%d %d\n",l,r-1);
}
return 0;
}
我在哪?
农夫约翰出门沿着马路散步,但是他现在发现自己可能迷路了!沿路有一排共 N个农场。不幸的是农场并没有编号,这使得约翰难以分辨他在这条路上所处的位置。然而,每个农场都沿路设有一个彩色的邮箱,所以约翰希望能够通过查看最近的几个邮箱的颜色来唯一确定他所在的位置。
每个邮箱的颜色用 A…Z之间的一个字母来指定,所以沿着道路的 N个邮箱的序列可以用一个长为 N的由字母 A…Z组成的字符串来表示。
某些邮箱可能会有相同的颜色。
约翰想要知道最小的 K的值,使得他查看任意连续 K个邮箱序列,他都可以唯一确定这一序列在道路上的位置。
例如,假设沿路的邮箱序列为 ABCDABC 。
约翰不能令 K=3,因为如果他看到了 ABC,则沿路有两个这一连续颜色序列可能所在的位置。
最小可行的 K的值为 K=4,因为如果他查看任意连续 4个邮箱,那么可得到的连续颜色序列可以唯一确定他在道路上的位置。
输入格式
输入的第一行包含 N,第二行包含一个由 N个字符组成的字符串,每个字符均在 A…Z之内。
输出格式
输出一行,包含一个整数,为可以解决农夫约翰的问题的最小 K值。
数据范围
1≤N≤100
输入样例:
7
ABCDABC
输出样例:
4
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
using namespace std;
const int N=110;
int n;
char s[N];
bool check(int mid){
unordered_map<string,int>h;
for(int i=mid;i<=n;++i){
string str="";
for(int j=i-mid+1;j<=i;++j)str+=s[j];
if(h[str])return false;
++h[str];
}
return true;
}
int main(){
scanf("%d%s",&n,s+1);
int l=1,r=n,ans=n;
while(l<r){
int mid=l+(r-l)/2;
if(check(mid))ans=r=mid;
else l=mid+1;
}
printf("%d\n",ans);
return 0;
}
四平方和
四平方和定理,又称为拉格朗日定理:
每个正整数都可以表示为至多 4个正整数的平方和。
如果把 0包括进去,就正好可以表示为 4个数的平方和。
对于一个给定的正整数,可能存在多种平方和的表示法。
要求你对 4个数排序:
0≤a≤b≤c≤d并对所有的可能表示法按 a,b,c,d为联合主键升序排列,最后输出第一个表示法。
输入格式
输入一个正整数 N。
输出格式
输出4个非负整数,按从小到大排序,中间用空格分开。
数据范围
0<N<5∗10^6
输入样例:
5
输出样例:
0 0 1 2
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<unordered_map>
using namespace std;
const int N=5e6+10,M=5e6;
int n,h[N];
void init(){
memset(h,-1,sizeof(h));
for(int i=0;i*i<=M;++i)
for(int j=i;j*j<=M-i*i;++j)
if(h[i*i+j*j]==-1)h[i*i+j*j]=i;
}
int main(){
init();
scanf("%d",&n);
for(int i=0;i*i<=n;++i){
for(int j=i;j*j<=n-i*i;++j){
int a=i*i+j*j;
int b=n-a;
if(h[b]!=-1){
printf("%d %d %d %d\n",i,j,h[b],(int)sqrt(b-h[b]*h[b]));
return 0;
}
}
}
return 0;
}
双指针
最长连续不重复子序列
给定一个长度为 n的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。
输入格式
第一行包含整数 n。
第二行包含 n个整数(均在 0∼105范围内),表示整数序列。
输出格式
共一行,包含一个整数,表示最长的不包含重复的数的连续区间的长度。
数据范围
1≤n≤10^5
输入样例:
5
1 2 2 3 5
输出样例:
3
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n,a[N];
int h[N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",&a[i]);
int ans=1;
for(int i=1,j=1;i<=n;++i){
if(h[a[i]]==0)++h[a[i]],ans=max(ans,i-j+1);
else {
while(j<i&&h[a[i]]>0)--h[a[j++]];
ans=max(ans,i-j+1);
++h[a[i]];
}
}
printf("%d\n",ans);
return 0;
}
数组元素的目标和
给定两个升序排序的有序数组 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≤数组元素≤109
输入样例:
4 5 6
1 2 4 7
3 4 6 8 9
输出样例:
1 1
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n,m,x,a[N],b[N];
int main(){
scanf("%d%d%d",&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]);
for(int i=0,j=m-1;i<n;++i){
while(a[i]+b[j]>x)--j;
if(a[i]+b[j]==x){
printf("%d %d\n",i,j);
break;
}
}
return 0;
}
判断子序列
给定一个长度为 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,
−10^9 ≤ai,bi≤10^9
输入样例:
3 5
1 3 5
1 2 3 4 5
输出样例:
Yes
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int n,m,a[N],b[N];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)scanf("%d",&a[i]);
for(int i=1;i<=m;++i)scanf("%d",&b[i]);
bool flag=true;
for(int i=1,j=1;flag&&i<=n;++i,++j){
while(j<=m&&a[i]!=b[j])++j;
if(j>m)flag=false;
}
if(flag)puts("Yes");
else puts("No");
return 0;
}
字符串删减
给定一个由 n个小写字母构成的字符串。
现在,需要删掉其中的一些字母,使得字符串中不存在连续三个或三个以上的 x。
请问,最少需要删掉多少个字母?
如果字符串本来就不存在连续的三个或三个以上 x,则无需删掉任何字母。
输入格式
第一行包含整数 n。
第二行包含一个长度为 n的由小写字母构成的字符串。
输出格式
输出最少需要删掉的字母个数。
数据范围
3≤n≤100
输入样例1:
6
xxxiii
输出样例1:
1
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=110;
int n;
char s[N];
int main(){
scanf("%d%s",&n,s+1);
int cnt=0,ans=0;
for(int i=1;i<=n;++i){
if(s[i]!='x')cnt=0;
else {
while(i<=n&&s[i]=='x')++i,++cnt;
ans+=max(0,cnt-2);
cnt=0;
}
}
printf("%d\n",ans);
return 0;
}
单调栈
排列
给你一个1-n的排列,每个<i,j>对的价值是j-i+1,计算所有满足以下条件<i,j>对的总价值:
- 1≤i<j≤n
- a[i]-a[j]之间的数字均小于min(a[i],a[j])
- a[i]-a[j]之间不存在其他数字则直接满足
输入格式
输入第一行包含正整数N(N≤300000)
第二行包含N个正整数,表示一个1-N的排列a
输出格式
输出一个正整数表示答案。
输入样例
7
4 3 1 2 5 6 7
**输出样例 **
24
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=3e5+10;
int n,a[N],stk[N],t;
LL res;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",&a[i]);
for(int i=1;i<=n;++i){
if(t==0)stk[++t]=i;
else {
while(t>0&&a[stk[t]]<a[i])
res+=i-stk[t--]+1;
if(t>0)res+=i-stk[t]+1;
stk[++t]=i;
}
}
printf("%lld\n",res);
return 0;
}
题目来源
https://www.acwing.com/
http://oj.ecustacm.cn/