蓝桥
前缀
*截断数组
给定一个长度为 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
输入样例2:
5
1 2 3 4 5
输出样例2:
0
输入样例3:
2
0 0
输出样例3:
0
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N=1e5+10;
typedef long long LL;
int a[N];
int s[N];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
s[i]=s[i-1]+a[i];
}
if(s[n]%3!=0)
{
cout<<"0"<<endl;
return 0;
}
LL res=0,cnt=0; //定义为LL类型
for(int j=2;j<n;j++)
{
if(s[j-1]==s[n]/3) cnt++;
if(s[j]==s[n]/3*2) res+=cnt;
}
cout<<res;
}
前缀和
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100010;
int n,m,a[N],s[N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
s[i]=s[i-1]+a[i];
}
while(m--)
{
int l,r;
cin>>l>>r;
cout<<s[r]-s[l-1]<<endl;
}
}
子矩阵的和
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,q;
const int N=1010;
int a[N][N],s[N][N];
int main()
{
cin>>n>>m>>q;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
s[i][j]=s[i][j-1]+s[i-1][j]-s[i-1][j-1]+a[i][j];
}
while(q--)
{
int x1,x2,y1,y2;
cin>>x1>>y1>>x2>>y2;
cout<<s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1]<<endl;
}
}
*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
输出样例:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100010;
typedef long long int LL;
LL a[N],s[N],cnt[N];
int main()
{
int count=0;
int n,k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
s[i]=s[i-1]+a[i];
}
LL res=0;
cnt[0]=1; //注意
for(int i=1;i<=n;i++)
{
res=res+cnt[s[i]%k];
cnt[s[i]%k]++;
}
printf("%lld",res);
return 0;
// cout<<count<<endl;
}
*激光炸弹
注意边界问题
地图上有 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<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=5010;
int n,m;
int s[N][N];
int main()
{
int cnt,R;
cin>>cnt>>R;
R=min(5001,R);
//n和m
n=m=R ;//保证右下角一定存在
while(cnt--)
{
int x,y,w;
cin>>x>>y>>w;
x++,y++;
n=max(n,x),m=max(m,y);
s[x][y]+=w;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
int res=0;
//枚举所有边长是R的矩形,右下角
for(int i=R;i<=n;i++)
for(int j=R;j<=m;j++)
res=max(res,s[i][j]-s[i-R][j]-s[i][j-R]+s[i-R][j-R]);
cout<<res<<endl;
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×105。
输入样例:
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<cstring>
#include<algorithm>
using namespace std;
const int N=2*1e5+10;
int a[N],v[N],s[N];
int main()
{
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++)
{
v[i+1]--;
int x=max(i-a[i]+1,1);
v[x]++;
}
// for(int i=1;i<=n;i++) cout<<v[i]<<" ";
// cout<<endl;
for(int i=1;i<=n;i++)
v[i]=v[i-1]+v[i];
for(int i=1;i<=n;i++ ) cout<<!!v[i]<<" ";
cout<<endl;
memset(v,0,sizeof v);
//memset(v,0,(n+1)*4);
}
}
差分
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
const int N=100010;
int a[N],b[N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
while(m--)
{
int l,r,c;
cin>>l>>r>>c;
b[l]+=c;
b[r+1]-=c;
}
for(int i=1;i<=n;i++) b[i]+=b[i-1];
for(int i=1;i<=n;i++) cout<<a[i]+b[i]<<" ";
}
差分矩阵
输入一个 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<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1010;
int a[N][N],b[N][N];
void insert(int x1,int y1,int x2,int y2,int c)
{
b[x1][y1]+=c;
b[x2+1][y1]-=c;
b[x1][y2+1]-=c;
b[x2+1][y2+1]+=c;
}
int main()
{
int n,m,q;
cin>>n>>m>>q;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>a[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;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]; //加自己
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++) cout<<b[i][j]<<' ';
cout<<endl;
}
}
*增减序列
二分
有二段性
*我在哪
暴力
农夫约翰出门沿着马路散步,但是他现在发现自己可能迷路了!沿路有一排共 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<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
string str;
int main()
{
int n;
cin>>n>>str;
for(int k=1;k<=n;k++)
{
bool flag=1;
for(int i=0; i+k-1<n;i++)
{
for(int j=i+1;j+k-1<n;j++)
{
bool same=true;
for(int u=0;u<k;u++)
{
if(str[i+u]!=str[j+u])
{
same=false;
break;
}
}
if(same==true)
{
flag=false;
break;
}
}
}
if(flag==1)
{
cout<<k<<endl;
break;
}
}
}
优化
数的范围
给定一个按照升序排列的长度为 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<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
const int N=100010;
using namespace std;
int a[N];
int n,m;
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
for(int i=0;i<m;i++)
{
int x;
cin>>x;
int l=0,r=n-1;
while(r>l)
{
int mid=r+l>>1;
if(a[mid]>=x) r=mid;
else l=mid+1;
}
if(a[l]==x)
{
cout<<l<<" ";
r=n-1;
while(r>l)
{
int mid=r+l+1>>1; //加1 l=mid
if(a[mid]<=x) l=mid;
else r=mid-1;
}
if(a[l]==x)
cout<<l<<endl;
}
else cout<<"-1 -1"<<endl;
}
}
四平方和
分巧克力
儿童节那天有 K 位小朋友到小明家做客。
小明拿出了珍藏的巧克力招待小朋友们。
小明一共有 N 块巧克力,其中第 i 块是 Hi×Wi 的方格组成的长方形。
为了公平起见,小明需要从这 N 块巧克力中切出 K 块巧克力分给小朋友们。
切出的巧克力需要满足:
形状是正方形,边长是整数大小相同
例如一块 6×5 的巧克力可以切出 6 块 2×2 的巧克力或者 2
块 3×3 的巧克力。
当然小朋友们都希望得到的巧克力尽可能大,你能帮小明计算出最大的边长是多少么?
输入格式
第一行包含两个整数 N 和 K。
以下 N 行每行包含两个整数 Hi 和 Wi。
输入保证每位小朋友至少能获得一块 1×1 的巧克力。
输出格式
输出切出的正方形巧克力最大可能的边长。
数据范围
1≤N,K≤105,
1≤Hi,Wi≤105
输入样例:
2 10
6 5
5 6
输出样例:
2
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int a[N],b[N];
int n,k,res;
bool check(int mid)
{
int sum=0;
for(int i=1;i<=n;i++)
{
sum+=(a[i]/mid)*(b[i]/mid);
}
if(sum>=k) return true;
else return false;
}
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i]>>b[i];
int l=1,r=1e5;
while(l<r)
{
int mid=r+l+1>>1;
if(check(mid))
l=mid;
else r=mid-1;
}
cout<<l<<endl;
}
*特殊排序
双指针
通用模板
for(int i=0,j=0;i<n;i++)
{
while(j<i&&check(i,j)) j++;
//具体逻辑
}
输出每个单词
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int main()
{
char str[1000];
gets(str);
int n=strlen(str);
for(int i=0;i<n;i++)
{
int j=i;
while(j<n&&str[j]!=' ')j++;
for(int k=i;k<j;k++) cout<<str[k];
cout<<endl;
i=j; //进入下一层循环,i++,指向非空字符
}
return 0;
}
输入
abc def as
输出
abc
def
as
字符串删减
给定一个由 n个小写字母构成的字符串。
现在,需要删掉其中的一些字母,使得字符串中不存在连续三个或三个以上的 x。
请问,最少需要删掉多少个字母?
如果字符串本来就不存在连续的三个或三个以上 x,则无需删掉任何字母。
输入格式
第一行包含整数 n。
第二行包含一个长度为 n 的由小写字母构成的字符串。
输出格式
输出最少需要删掉的字母个数。
数据范围
3≤n≤100
输入样例1:
6
xxxiii
输出样例1:
1
输入样例2:
5
xxoxx
输出样例2:
0
输入样例3:
10
xxxxxxxxxx
输出样例3:
8
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
string str;
int main()
{
int n;
cin>>n;
cin>>str;
int sum=0,res=0;
int i=0,j=1;
while(j<n)
{
if(str[i]==str[j]&&str[i]=='x')
{
sum++;
j++;
if(sum>=2) res++;
}
else
{
i=j;
j++;
sum=0;
}
}
cout<<res<<endl;
}
日志统计
小明维护着一个程序员论坛。现在他收集了一份”点赞”日志,日志共有 N 行。
其中每一行的格式是:
ts id 表示在 ts 时刻编号 id 的帖子收到一个”赞”。
现在小明想统计有哪些帖子曾经是”热帖”。
如果一个帖子曾在任意一个长度为 D 的时间段内收到不少于 K 个赞,小明就认为这个帖子曾是”热帖”。
具体来说,如果存在某个时刻 T 满足该帖在 [T,T+D) 这段时间内(注意是左闭右开区间)收到不少于 K 个赞,该帖就曾是”热帖”。
给定日志,请你帮助小明统计出所有曾是”热帖”的帖子编号。
输入格式
第一行包含三个整数 N,D,K。
以下 N 行每行一条日志,包含两个整数 ts 和 id。
输出格式
按从小到大的顺序输出热帖 id。
每个 id 占一行。
数据范围
1≤K≤N≤105,
0≤ts,id≤105,
1≤D≤10000
输入样例:
7 10 2
0 1
0 10
10 10
10 1
9 1
100 3
100 3
输出样例:
1
3
#include<iostream>
#include<cstring>
#include<algorithm>
#define x first
#define y second
using namespace std;
int n,d,k;
typedef pair<int,int >PII;
const int N=1e5+10;
PII a[N];
bool st[N];
int cnt[N];
int main()
{
cin>>n>>d>>k;
for(int i=0;i<n;i++) cin>>a[i].x>>a[i].y;
sort(a,a+n);
for(int i=0,j=0;i<n;i++)
{
int id=a[i].y;
cnt[id]++;
while(a[i].x-a[j].x>=d)
{
cnt[a[j].y]--;
j++; //区间左端点前移一位
}
if(cnt[id]>=k) st[id]=1; //标记热帖
}
for(int i=0;i<1e5;i++)
if(st[i]==true) cout<<i<<endl;
}
完全二叉树的权值
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int a[N];
const int INF=1e12; //注意最小值
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
long long int max=-INF;
long long int depth=0;
for(int i=1,d=1;i<=n;i=i*2,d++) //d表示层数 i表示每层开始第一个数的下标
{
long long int sum=0;
for(int j=i;j<i*2&&j<=n;j++) //j<=n
sum+=a[j];
if(sum>max)
{
max=sum;
depth=d;
}
}
cout<<depth;
}
最长连续不重复子序列
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100010;
int n;
int a[N],s[N];
int main()
{
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
int res=0;
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;
}
输入
5
1 2 2 3 5
输出
3
数组元素的目标和
判断子序列
位运算
1、把第k位移到最后一位 n>>k;
2、看一下第k位是几n>>k&1;
树状数组
lowbit(x) : 返回x的最后一个1;
x&-x
x=1010001000
~x=0101110111
~x+1=0101111111
x&(~x+1)=0000001000
应用:
统计x中1的个数
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int lowbit(int x)
{
return x&-x;
}
int main()
{
int n;
cin>>n;
while(n--)
{
int x;
cin>>x;
int res=0;
while(x) x-=lowbit(x),res++; //每次减去x的最后一位1
cout<<res<<" ";
}
return 0;
}
输入
5
1 2 3 4 5
输出
1 1 2 1 2
递推
*砖块
n 个砖块排成一排,从左到右编号依次为 1∼n。
每个砖块要么是黑色的,要么是白色的。
现在你可以进行以下操作若干次(可以是 0 次):
选择两个相邻的砖块,反转它们的颜色。(黑变白,白变黑)
你的目标是通过不超过 3n 次操作,将所有砖块的颜色变得一致。
输入格式
第一行包含整数 T,表示共有 T 组测试数据。
每组数据第一行包含一个整数 n。
第二行包含一个长度为 n 的字符串 s。其中的每个字符都是 W 或 B,如果第 i 个字符是 W,则表示第 i 号砖块是白色的,如果第 i 个字符是 B,则表示第 i个砖块是黑色的。
输出格式
每组数据,如果无解则输出一行 −1。
否则,首先输出一行 k,表示需要的操作次数。
如果 k>0,则还需再输出一行 k 个整数,p1,p2,…,pk。其中 pi 表示第 i 次操作,选中的砖块为 pi 和 pi+1 号砖块。
如果方案不唯一,则输出任意合理方案即可。
数据范围
1≤T≤10,2≤n≤200。
输入样例:
4
8
BWWWWWWB
4
BWBB
5
WWWWW
3
BWB
输出样例:
3
6 2 4
-1
0
2
2 1
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
string str;
int n;
void update(char &c)
{
if(c=='W') c='B';
else c='W';
}
bool check(char c)
{
vector<int> res;
string s=str;
for(int i=0;i<n-1;i++) //i+1<n;
{
if(s[i]!=c)
{
update(s[i]);
update(s[i+1]);
res.push_back(i);
}
}
//if(s.back()!=c) return false;
if(s[n-1]!=c) return false;
cout<<res.size()<<endl;
for(int x:res ) cout<<x+1<<" ";
if(res.size()) cout<<endl;
return true;
}
int main()
{
int t;
cin>>t;
while(t--)
{
cin>>n>>str;
if(!check('B')&&!check('W')) cout<<"-1"<<endl;
}
}
翻硬币
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
string s1,s2;
void turn (int x)
{
if(s1[x]=='*') s1[x]='o';
else s1[x]='*';
}
int main()
{
cin>>s1>>s2;
int res=0;
for(int i=0;i<s1.size();i++)
{
if(s1[i]!=s2[i])
{
turn(i);
turn(i+1);
res++;
}
}
cout<<res;
}