二分与前缀和
二分
整数二分
整数二分要注意边界,最好背过一套模板
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”。
扫描二维码关注公众号,回复:
12412761 查看本文章

数据范围:
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<iostream>
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<iostream>
#include<cstdio>
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=Sx−1,y+Sx,y−1−Sx−1,y−1+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,y2−Sx2,y1−1−Sx1−1,y2+Sx1−1,y1−1
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<iostream>
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;
}