二分与前缀和

目录

数的范围

输入格式

输出格式

数据范围

输入样例:

输出样例:

 数的三次方根

输入格式

输出格式

数据范围

 机器人跳跃问题

输入格式

输出格式

数据范围

输入样例1:

输出样例1:

输入样例2:

输出样例2:

输入样例3:

输出样例3:

四平方和 

输入格式

输出格式

输入输出样例

说明/提示

分巧克力

输入格式

输出格式

输入输出样例

说明/提示

前缀和 

输入格式

输出格式

数据范围

输入样例:

输出样例:

子矩阵的和 

输入格式

输出格式

数据范围

输入样例:

输出样例:

激光炸弹 

输入格式

输出格式

数据范围

输入样例:

输出样例:

K倍区间 

题目描述

输入格式

输出格式

输入输出样例

说明/提示


数的范围

给定一个按照升序排列的长度为 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 块巧克力分给小朋友们。切出的巧克力需要满足:

  1. 形状是正方形,边长是整数。

  2. 大小相同。

例如一块 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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值