目录
数的范围
给定一个按照升序排列的长度为 n� 的整数数组,以及 q� 个查询。
对于每个查询,返回一个元素 k� 的起始位置和终止位置(位置从 00 开始计数)。
如果数组中不存在该元素,则返回
-1 -1
。输入格式
第一行包含整数 n� 和 q�,表示数组长度和询问个数。
第二行包含 n� 个整数(均在 1∼100001∼10000 范围内),表示完整数组。
接下来 q� 行,每行包含一个整数 k�,表示一个询问元素。
输出格式
共 q� 行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回
-1 -1
。数据范围
1≤n≤1000001≤�≤100000
1≤q≤100001≤�≤10000
1≤k≤100001≤�≤10000输入样例:
6 3 1 2 2 3 3 4 3 4 5
输出样例:
3 4 5 5 -1 -1
熟悉函数的使用
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,m;
int a[100005];
cin>>n>>m;
for(int i=0;i<n;i++)cin>>a[i];
while(m--){
int x;
cin>>x;
int l=lower_bound(a,a+n,x)-a;
int r=upper_bound(a,a+n,x)-a;
if(a[l]!=x)l=-1,r=0;
cout<<l<<" "<<r-1<<"\n";
}
return 0;
}
手写二分
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,m;
int a[100005];
cin>>n>>m;
for(int i=0;i<n;i++)cin>>a[i];
while(m--){
int x;
cin>>x;
int l=0,r=n-1;
while(l<r){
int mid=(l+r+1)/2;
if(a[mid]<=x)l=mid;
else r=mid-1;
}
if(a[l]!=x){
cout<<-1<<' '<<-1<<"\n";
}
else{
int p=l;
l=0,r=n-1;
while(l<r){
int mid=(l+r)/2;
if(a[mid]>=x)r=mid;
else l=mid+1;
}
cout<<l<<' '<<p<<"\n";
}
}
return 0;
}
数的三次方根
给定一个浮点数 n�,求它的三次方根。
输入格式
共一行,包含一个浮点数 n�。
输出格式
共一行,包含一个浮点数,表示问题的解。
注意,结果保留 66 位小数。
数据范围
−10000≤n≤10000
#include<bits/stdc++.h>
using namespace std;
int main(){
double a,l=-100.0,r=100.0;
cin>>a;
while(r-l>0.0000001){//左右边界在一个可容纳的精度内
double mid=(l+r)/2;
if(mid*mid*mid>=a)r=mid;
else l=mid;
}
printf("%.6f",l);
return 0;
}
机器人跳跃问题
机器人正在玩一个古老的基于 DOS 的游戏。
游戏中有 N+1�+1 座建筑——从 00 到 N� 编号,从左到右排列。
编号为 00 的建筑高度为 00 个单位,编号为 i� 的建筑高度为 H(i)�(�) 个单位。
起初,机器人在编号为 00 的建筑处。
每一步,它跳到下一个(右边)建筑。
假设机器人在第 k� 个建筑,且它现在的能量值是 E�,下一步它将跳到第 k+1�+1 个建筑。
如果 H(k+1)>E�(�+1)>�,那么机器人就失去 H(k+1)−E�(�+1)−� 的能量值,否则它将得到 E−H(k+1)�−�(�+1) 的能量值。
游戏目标是到达第 N� 个建筑,在这个过程中能量值不能为负数个单位。
现在的问题是机器人至少以多少能量值开始游戏,才可以保证成功完成游戏?
输入格式
第一行输入整数 N�。
第二行是 N� 个空格分隔的整数,H(1),H(2),…,H(N)�(1),�(2),…,�(�) 代表建筑物的高度。
输出格式
输出一个整数,表示所需的最少单位的初始能量值上取整后的结果。
数据范围
1≤N,H(i)≤1051≤�,�(�)≤105,
输入样例1:
5 3 4 3 2 4
输出样例1:
4
输入样例2:
3 4 4 4
输出样例2:
4
输入样例3:
3 1 6 4
输出样例3:
3
随手推一下即可知道,无论高度是否大于能量值,计算公式都是2*能量值-当前高度
递推算法:要使结果最小,那么最后得到的能量值一定是0,那么可以O(n)倒推一遍
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,e;
int a[100005];
int main() {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
for(int i=n-1;i>=0;i--){
e=(e+a[i]+1)/2;//+1是为了向上取整,也可以直接将e定为double类型,然后向上取整即可
}
cout<<e;
return 0;
}
二分写法:二分答案
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n, sum;
int a[100005];
bool check(int x) {
for (int i = 0; i < n; i++) {
x = 2 * x - a[i];
if (x > sum)return 1;//如果x比所有数组和都大了,那么一定可以满足结果
if (x < 0)return 0;//不满足的情况
}
return 1;
}
int main() {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
sum += a[i];
}
int l = 0, r = sum;
while (l < r) {//直到找到临界点
int mid = (l + r) / 2;
if (check(mid))r = mid;//r始终是可以满足情况的条件
else l = mid + 1;//不断变大不满足的值,
}
cout << l;
return 0;
}
四平方和
四平方和定理,又称为拉格朗日定理:
每个正整数都可以表示为至多 44 个正整数的平方和。
如果把 00 包括进去,就正好可以表示为 44 个数的平方和。
比如:
5=02+02+12+225=02+02+12+22。
7=12+12+12+227=12+12+12+22。
对于一个给定的正整数,可能存在多种平方和的表示法。
要求你对 44 个数排序使得 0≤�≤�≤�≤�0≤a≤b≤c≤d。
并对所有的可能表示法按 �,�,�,�a,b,c,d 为联合主键升序排列,最后输出第一个表示法。
输入格式
程序输入为一个正整数 �(�<5×106)N(N<5×106)。
输出格式
要求输出 44 个非负整数,按从小到大排序,中间用空格分开。
输入输出样例
输入 #1复制
5输出 #1复制
0 0 1 2输入 #2复制
12输出 #2复制
0 2 2 2输入 #3复制
773535输出 #3复制
1 1 267 838说明/提示
时限 3 秒, 256M。蓝桥杯 2016 年第七届省赛
蓝桥杯 2016 年省赛 A 组 H 题(B 组 H 题)。
暴力就过了???确实过了
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n;
int main() {
cin >> n;
for (int i = 0; i * i < n; i++) {
for (int j = 0; j * j < n - i * i; j++) {
for (int k = 0; k * k < n - i * i - j * j; k++) {
int x = n - i * i - j * j - k * k;
int p = sqrt(x);
if (p * p == x) {
cout << i << ' ' << j << ' ' << k << ' ' << p << "\n";
return 0;
}
}
}
}
return 0;
}
分巧克力
儿童节那天有 �K 位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。
小明一共有 �N 块巧克力,其中第 �i 块是 ��×��Hi×Wi 的方格组成的长方形。
为了公平起见,小明需要从这 �N 块巧克力中切出 �K 块巧克力分给小朋友们。切出的巧克力需要满足:
形状是正方形,边长是整数。
大小相同。
例如一块 6×56×5 的巧克力可以切出 66 块 2×22×2 的巧克力或者 22 块 3×33×3 的巧克力。
当然小朋友们都希望得到的巧克力尽可能大,你能帮小 ��Hi 计算出最大的边长是多少么?
输入格式
第一行包含两个整数 �N 和 �K。(1≤�,�≤105)(1≤N,K≤105)。
以下 �N 行每行包含两个整数 ��Hi 和 ��Wi。(1≤��,��≤105)(1≤Hi,Wi≤105)。
输入保证每位小朋友至少能获得一块 1×11×1 的巧克力。
输出格式
输出切出的正方形巧克力最大可能的边长。
输入输出样例
输入 #1复制
2 10 6 5 5 6输出 #1复制
2说明/提示
蓝桥杯 2022 省赛 A 组 I 题。
二分答案,找最大值
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n, m;
int sum;
int x[100005], y[100005];
bool check(int p) {
int cnt = 0;
for (int i = 0; i < n; i++) {
cnt += (x[i] / p) * (y[i] / p);//记得打括号,不然会错,可能是因为精度缺失趴
}
return cnt >= m;
}
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++) {
cin >> x[i] >> y[i];
sum += x[i]* y[i];
}
int l = 1, r = sum;
while (l < r) {
int mid = (l + r + 1) / 2;
if (check(mid))l = mid;//成立则左边变大
else r = mid - 1;//不成立则右边变小
}
cout << l;
return 0;
}
前缀和
输入一个长度为 n� 的整数序列。
接下来再输入 m� 个询问,每个询问输入一对 l,r�,�。
对于每个询问,输出原序列中从第 l� 个数到第 r� 个数的和。
输入格式
第一行包含两个整数 n� 和 m�。
第二行包含 n� 个整数,表示整数数列。
接下来 m� 行,每行包含两个整数 l� 和 r�,表示一个询问的区间范围。
输出格式
共 m� 行,每行输出一个询问的结果。
数据范围
1≤l≤r≤n1≤�≤�≤�,
1≤n,m≤1000001≤�,�≤100000,
−1000≤数列中元素的值≤1000−1000≤数列中元素的值≤1000输入样例:
5 3 2 1 3 6 4 1 2 1 3 2 4
输出样例:
3 6 10
一维前缀和
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int a[100005];
int main() {
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
a[i]+=a[i-1];//前面所有数的和
}
while(m--){
int l,r;
cin>>l>>r;
cout<<a[r]-a[l-1]<<'\n';
}
return 0;
}
子矩阵的和
输入一个 n� 行 m� 列的整数矩阵,再输入 q� 个询问,每个询问包含四个整数 x1,y1,x2,y2�1,�1,�2,�2,表示一个子矩阵的左上角坐标和右下角坐标。
对于每个询问输出子矩阵中所有数的和。
输入格式
第一行包含三个整数 n,m,q�,�,�。
接下来 n� 行,每行包含 m� 个整数,表示整数矩阵。
接下来 q� 行,每行包含四个整数 x1,y1,x2,y2�1,�1,�2,�2,表示一组询问。
输出格式
共 q� 行,每行输出一个询问的结果。
数据范围
1≤n,m≤10001≤�,�≤1000,
1≤q≤2000001≤�≤200000,
1≤x1≤x2≤n1≤�1≤�2≤�,
1≤y1≤y2≤m1≤�1≤�2≤�,
−1000≤矩阵内元素的值≤1000−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<bits/stdc++.h>
using namespace std;
#define ll long long
int a[1005][1005];
int main() {
int n,m,k;
cin>>n>>m>>k;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
a[i][j]+=a[i][j-1]+a[i-1][j]-a[i-1][j-1];
}
}
while(k--){
int l,r,x,y;
cin>>l>>r>>x>>y;
cout<<a[x][y]-a[l-1][y]-a[x][r-1]+a[l-1][r-1]<<'\n';
}
return 0;
}
激光炸弹
地图上有 N� 个目标,用整数 Xi,Yi��,�� 表示目标在地图上的位置,每个目标都有一个价值 Wi��。
注意:不同目标可能在同一位置。
现在有一种新型的激光炸弹,可以摧毁一个包含 R×R�×� 个位置的正方形内的所有目标。
激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆炸范围,即那个正方形的边必须和 x,y�,� 轴平行。
求一颗炸弹最多能炸掉地图上总价值为多少的目标。
输入格式
第一行输入正整数 N� 和 R�,分别代表地图上的目标数目和正方形包含的横纵位置数量,数据用空格隔开。
接下来 N� 行,每行输入一组数据,每组数据包括三个整数 Xi,Yi,Wi��,��,��,分别代表目标的 x� 坐标,y� 坐标和价值,数据用空格隔开。
输出格式
输出一个正整数,代表一颗炸弹最多能炸掉地图上目标的总价值数目。
数据范围
0≤R≤1090≤�≤109
0<N≤100000<�≤10000,
0≤Xi,Yi≤50000≤��,��≤5000
0≤Wi≤10000≤��≤1000输入样例:
2 1 0 0 1 1 1 1
输出样例:
1
考察了二维前缀和的应用
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int t, r, ans;
int a[5005][5005];
int main() {
cin >> t >> r;
if(r>=5001)r=5001;//如果r很大,直接赋为整个地图
while(t--) {
int x, y, w;
cin >> x >> y >> w;
a[x + 1][y + 1] += w;//将x,y从0~5000变成1~5001
}
for (int i = 1; i <= 5001; i++) {
for (int j = 1; j <= 5001; j++) {
a[i][j] += a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];
}
}
for (int i = r; i <= 5001; i++) {
for (int j = r; j <= 5001; j++) {
ans = max(ans, a[i][j] - a[i - r][j] - a[i][j - r] + a[i - r][j - r]);
}
}
cout << ans;
return 0;
}
K倍区间
题目描述
给定一个长度为 �N 的数列,�1,�2,⋯��A1,A2,⋯AN,如果其中一段连续的子序列 ��,��+1,⋯��(�≤�)Ai,Ai+1,⋯Aj(i≤j) 之和是 �K 的倍数,我们就称这个区间 [�,�][i,j] 是 �K 倍区间。
你能求出数列中总共有多少个 �K 倍区间吗?
输入格式
第一行包含两个整数 �N 和 �K(1≤�,�≤105)(1≤N,K≤105)。
以下 �N 行每行包含一个整数 ��Ai(1≤��≤105)(1≤Ai≤105)。
输出格式
输出一个整数,代表 �K 倍区间的数目。
输入输出样例
输入 #1复制
5 2 1 2 3 4 5输出 #1复制
6说明/提示
时限 2 秒, 256M。蓝桥杯 2017 年第八届
由于数据范围很大,所以需要优化
对于(R-L)%k==0,可以得到R%k==L%k(同余定理)
所以只需要枚举一遍即可,当枚举到i时,将其默认为R边界,统计完之后,将它作为左边界
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll a[100005];
ll v[100005];
ll cnt;
int main() {
int n, k;
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> a[i];
a[i] += a[i - 1];//前缀和
}
for (int i = 0; i <= n; i++) {//表示区间长度,可以为0
cnt += v[a[i] % k];//当a[i]为右边界时,前面和它同余的个数
v[a[i] % k]++;//将a[i]当成左边界,左边界同余个数+1
}
cout << cnt;
return 0;
}