一、二维前缀和/区间和
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=si−1,j+si,j−1+ai,j−si−1,j−1
二维区间和(准确地说,是任意矩阵区域内的所有数字之和)公式为:
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,y1∼x2,y2=sx2,y2−sx1−1,y2−sx2,y1−1+sx1−1,y1−1
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
函数。
第一个参数的常用值如下:
参数 | 意义 |
---|---|
%d | int |
%lld | long long |
空格+%c | char |
%lf | double |
二、差分
典型例题:
给定一个长度为 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;
}