二分和前缀和
二分的两种格式(边界不要不要搞混)
1)若数组中存在多个相同的元素,则该方法可求出最后一次出现的位置
while(L < R){
M = (L + R + 1) / 2;
if(M <= ans){ //左半个区间包括了M
L = M;
}else{
R = M - 1;
}
}
2)若数组中存在多个相同的元素,则该方法可求出首次出现的位置
while(L < R){
M = (L + R) / 2;
if(M < ans){ //左半个区间包括了M
L = M + 1;
}else{
R = M;
}
}
例:利用二分求三次方根
#include
#include
#include
#include
using namespace std;
int main(){
double L = -10000, R = 10000;
double n;
scanf("%lf", &n);
while(R - L > 1e-8){
double M = (L + R) / 2;
if(M * M * M >= n){
R = M;
}else{
L = M;
}
}
printf("%.6lf", L);
return 0;
}
前缀和
1)一维前缀和
将求从第m~n个数的和从O(n)降到O(1)
S = Sn -Sm, 只需要开始时用一个数组存放所求的和
2)二维前缀和(容斥原理)
原矩阵:
*1 | *7 | *2 | 4 |
---|---|---|---|
*3 | *6 | *2 | 8 |
2 | 1 | 2 | 3 |
求和公式(利用容斥原理):S(x, y) = S(x - 1, y) + S(x, y - 1) - S(x - 1, y - 1) + cur(x, y)
例如:如上*所标数字求S(2, 3), 2 * 3表格内的数字之和 = (2, 3)左边的 2 * 2表格 + (2, 3)上面的1 * 3表格 - 重叠部分的1 * 2表格 + 当前位置的数字
求和后矩阵:
1 | 8 | 10 | 14 |
---|---|---|---|
4 | 17 | 21 | 33 |
6 | 20 | 26 | 41 |
进而利用容斥原理求二维的前缀和,即求从(x1, y1)到(x2, y2)的前缀和, 公式如下:
S = S(x2, y2) - S(x2, y1 - 1) - S(x1 - 1, y2) + S(x1 - 1, y1 - 1)
例:求子矩阵的和(知道矩阵的一个对角线上的两个端点即可确定子矩阵)
第一行包含三个整数 n,m,q。
接下来 n 行,每行包含 m 个整数,表示整数矩阵。
接下来 q 行,每行包含四个整数 x1,y1,x2,y2,表示一组询问。
#include
#include
#include
#include
using namespace std;
int nums[1010][1010];
int main(){
int m, n, q;
scanf("%d%d%d", &m, &n, &q);
for(int i = 1; i <= m; i++)
for(int j = 1; j <= n; j++){
int cur;
scanf("%d", &cur);
//矩阵求和的公式:
//S(x, y) = S(x - 1, y) + S(x, y - 1) - S(x - 1, y - 1) + cur(x, y)
// printf("%d ", cur);
nums[i][j] = nums[i - 1][j] + nums[i][j - 1] - nums[i - 1][j - 1] + cur;
// printf("%d ", nums[i][j]);
}
while(q--){
int x1, x2, y1, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
int ans = nums[x2][y2] - nums[x1 - 1][y2] - nums[x2][y1 - 1] + nums[x1 - 1][y1 - 1];
printf("%d\n", ans);
}
return 0;
}