二分与前缀和 学习笔记

二分与前缀和

二分

整数二分

整数二分要注意边界,最好背过一套模板

bool check(int x) {
     
    /*检查 x 是否满足要求*/
}

// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
void bsearch_1(int l, int r) {
     
    while (l < r) {
     
        int mid = (l + r + 1) >> 1;
        if (check(mid))
            l = mid;
        else
            r = mid - 1;
    }
}
// 区间[l, r]被划分成[mid + 1, r]和[l, mid]时使用:
void bsearch_1(int l, int r) {
     
    while (l < r) {
     
        int mid = (l + r) >> 1;
        if (check(mid))
            l = mid + 1;
        else
            r = mid;
    }
}
789. 数的范围

给定一个按照升序排列的长度为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

using namespace std;
const int N = 100001;
int a[N];

int main() {
     
    int n, k, x;
    cin >> n >> k;
    for (int i = 0; i < n; i++) {
     
        cin >> a[i];
    }
    for (int i = 0; i < k; i++) {
     
        cin >> x;
        //先找左端点
        int l = 0, r = n - 1;
        while (l < r) {
     
            int mid = (l + r) >> 1;
            if (a[mid] >= x)
                r = mid;
            else
                l = mid + 1;
        }
        if (a[r] == x) {
     
            cout << r << " ";
            //找到左端点后找右端点
            r = n - 1;
            while (l < r) {
     
                int mid = (l + r + 1) >> 1;
                if (a[mid] <= x)
                    l = mid;
                else
                    r = mid - 1;
            }
            cout << l << endl;
        } else//找不到左端点直接输出 -1 -1
            cout << "-1 -1" << endl;
    }
    return 0;
}

浮点数二分

相对整数二分来说简单多了~ 没有坑人的边界问题

bool check(int x) {
     
    /*检查 x 是否满足要求*/
}

void bserach(double l, double r) {
     
    while (r - l > 1e6) {
     //题目要求精度
        double mid = (l + r) / 2;
        if (check(mid))
            r = mid;
        else
            l = mid;
    }
}
790. 数的三次方根

给定一个浮点数n,求它的三次方根。

输入格式:

共一行,包含一个浮点数n。

输出格式

共一行,包含一个浮点数,表示问题的解。

注意,结果保留6位小数。

数据范围:

−10000 ≤ n ≤ 10000

输入样例:

1000.00

输出样例:

10.000000

代码:

#include
#include

using namespace std;

int main() {
     
    double n;
    bool fu = false;
    cin >> n;
    if (n < 0) {
     
        fu = true;
        n *= (-1);
    }
    double l = 0, r = 10001;
    double mid;
    while (r - l > 1e-8) {
     
        mid = (l + r) / 2;
        if (mid * mid * mid > n)
            r = mid;
        else
            l = mid;
    }
    if (fu)
        mid *= (-1);
    printf("%.6f", mid);
    return 0;
}

前缀和

  • 前缀和矩阵 S x y = S x − 1 , y + S x , y − 1 − S x − 1 , y − 1 + a x , y S_{xy} = S_{x-1,y} + S_{x,y-1} - S_{x-1,y-1} + a_{x,y} Sxy=Sx1,y+Sx,y1Sx1,y1+ax,y (容斥原理)
  • 利用前缀和矩阵计算子矩阵的和 [ x 1 y 1 , x 2 y 2 ] = S x 2 , y 2 − S x 2 , y 1 − 1 − S x 1 − 1 , y 2 + S x 1 − 1 , y 1 − 1 [x_1y_1,x_2y_2] = S_{x_2,y_2} - S_{x_2,y_1-1} - S_{x_1-1,y_2} + S_{x_1-1,y_1-1} [x1y1,x2y2]=Sx2,y2Sx2,y11Sx11,y2+Sx11,y11
1230. 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

using namespace std;
const int N = 100010;
long long int n, k, x, ans = 0;
long long int s[N];
long long int res[N];

int main() {
     
    cin >> n >> k;
    res[0] = 1;
    //s[0]%k=0,故余数为0的数已经有一个了
    for (long long int i = 1; i <= n; i++) {
     
        cin >> x;
        s[i] = s[i - 1] + x;
        ans += res[s[i] % k];
        res[s[i] % k]++;
    }
    cout << ans << endl;
    return 0;
} 

你可能感兴趣的:(算法笔记,c++,算法,二分查找,前缀和)