2024年CSP-J暑假冲刺训练营(7):前缀和/差分进阶、二分模板/二分答案

一、二维前缀和/区间和

1. 公式

首先我们要明确,二维前缀和所求解的是:
s i , j = a 1 , 1 + ⋯ + a i , j s_{i,j}=a_{1,1}+\cdots+a_{i,j} si,j=a1,1++ai,j

因此二维前缀和公式为:
s i , j = s i − 1 , j + s i , j − 1 + a i , j − s i − 1 , j − 1 s_{i,j}=s_{i-1,j}+s_{i,j-1}+a_{i,j}-s_{i-1,j-1} si,j=si1,j+si,j1+ai,jsi1,j1
二维区间和(准确地说,是任意矩阵区域内的所有数字之和)公式为:
s x 1 , y 1 ∼ x 2 , y 2 = s x 2 , y 2 − s x 1 − 1 , y 2 − s x 2 , y 1 − 1 + s x 1 − 1 , y 1 − 1 s_{x_1,y_1\sim x_2,y_2}=s_{x_2,y_2}-s_{x_1-1,y_2}-s_{x_2,y_1-1}+s_{x_1-1,y_1-1} sx1,y1x2,y2=sx2,y2sx11,y2sx2,y11+sx11,y11

2. 模板

#include <iostream>
using namespace std;

int n, m, t;
int a[110][110];
int s[110][110];
int x1, y1, x2, y2;

int main() {
    cin >> n >> m;
    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]+a[i][j]-s[i-1][j-1];
        }
    cin >> t;
    while (t--) {
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        cout << s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1] << "\n";
    }
	return 0;
}

注意:在大规模输入的时候,我们一般会使用 scanf 函数。

第一个参数的常用值如下:

参数意义
%dint
%lldlong long
空格+%cchar
%lfdouble

二、差分

典型例题:

给定一个长度为 n n n 的数列 a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots,a_n a1,a2,,an,每次可以选择一个区间 [ l , r ] [l,r] [l,r],使这个区间内的数都加 1 1 1 或者都减 1 1 1。请问至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列有多少种。

这道题目需要用到差分,我们可以找到一种方法,让差分数组进行如下变化:
b 1 , b 2 , ⋯   , b n       →       b 1 , 0 , ⋯   , 0 b_1,b_2,\cdots,b_n\ \ \ \ \ \to\ \ \ \ \ b_1,0,\cdots,0 b1,b2,,bn          b1,0,,0

#include <iostream>
#include <cmath>
using namespace std;

long long n, z, f;
int a[100100];
int b[100100];

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        b[i] = a[i] - a[i-1];
    }
    for (int i = 2; i <= n; i++) {
        if (b[i] < 0) f += b[i];
        else z += b[i];
    }
    f *= -1;
    cout << max(z, abs(f)) << endl << abs(z-f)+1;
    return 0;
}

三、二分查找

1. 特点

  • 优点

    • 二分查找的时间复杂度较低,尤其在大型有序序列中效果更为显著。
    • 操作简单,容易实现。
  • 缺点

    • 二分查找要求查找序列是有序的,如果序列未排序,则需要进行排序操作,增加了时间复杂度。
    • 对于频繁插入、删除操作的动态数据集,二分查找的效率较低。
    • 额外的空间复杂度较高,需要额外的数组来存储有序序列。

2. 模板

2.1 搜索模板

#include <iostream>
using namespace std;

int n, m, k;
int a[100100];

int binarySearch(int x) {
    int l = 1, r = n;
    while (l <= r) {
        int mid = (l+r)/2;
        if (a[mid] < x) l = mid + 1;
        else if (a[mid] > x) r = mid - 1;
        else return mid;
    }
    return -1;
}

int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) cin >> a[i];
	while (m--) {
	    cin >> k;
	    cout << binarySearch(k) << " ";
	}
	return 0;
}

2.2 方根模板

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

考试的时候直接输出:
ans = n 1 5 \text{ans}=n^{\frac{1}{5}} ans=n51

参考程序如下:

#include <iostream>
#include <cstdio>
using namespace std;

double n, l = 0, r = 10;

int main() {
    freopen("root.in", "r", stdin);
    freopen("root.out", "w", stdout);
    
	cin >> n;
	while (r - l > 0.00000001) {
	    double mid = (l+r)/2;
	    if (mid*mid*mid*mid*mid < n) l = mid;
	    else r = mid;
	}
	cout << l;
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}

四、二分答案

1. 问题特点

  • 题目问法
    最大的最小值 / / / 最小的最大值
  • 优点
    很好判断是否合法

2. 例题

愤怒的羊驼

动物园来了 C C C 只羊驼,为此动物园需要建造一个有 N N N 个隔间的棚子,这些隔间分布在一条直线上,坐标是 x 1 , x 2 , ⋯   , x n x_1,x_2,\cdots,x_n x1,x2,,xn。羊驼在隔间的位置分布必须合理,不然羊驼会认为自己处于危险之中,开始互相吐口水来保护自己,那整个动物园将会臭气熏天!所以为了让羊驼感到安全,在把它们安置在指定的隔间时,所有羊驼中相邻两只的最近距离越大越好。那么,这个最大的最近距离是多少?

#include <iostream>
#include <algorithm>
using namespace std;

int ans;
int n, c;
int l, r;
int a[100010];

bool check(int x) {
    int last = a[1], cnt = 1;
    for (int i = 2; i <= n; i++)
        if (a[i] - last >= x) {
            cnt++;
            last = a[i];
        }
    return (cnt >= c);
}

int main() {
	cin >> n >> c;
	for (int i = 1; i <= n; i++) cin >> a[i];
	sort(a+1, a+n+1);
	
	l = 1, r = a[n]-a[1];
	while (l <= r) {
	    int mid = (l+r)/2;
	    if (check(mid)) {
	        ans = mid;
	        l = mid + 1;
	    } else {
	        r = mid - 1;
	    }
	}
	
	cout << ans;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值