题目背景
上一题“序列查询”中说道:
A=[A0,A1,A2,⋯,An] 是一个由 n+1 个 [0,N) 范围内整数组成的序列,满足 0=A0<A1<A2<⋯<An<N。基于序列 A,对于 [0,N) 范围内任意的整数 x,查询 f(x) 定义为:序列 A 中小于等于 x 的整数里最大的数的下标。
对于给定的序列 A 和整数 x,查询 f(x) 是一个很经典的问题,可以使用二分搜索在 O(logn) 的时间复杂度内轻松解决。但在 IT 部门讨论如何实现这一功能时,小 P 同学提出了些新的想法。
题目描述
小 P 同学认为,如果事先知道了序列 A 中整数的分布情况,就能直接估计出其中小于等于 x 的最大整数的大致位置。接着从这一估计位置开始线性查找,锁定 f(x)。如果估计得足够准确,线性查找的时间开销可能比二分查找算法更小。
比如说,如果 A1,A2,⋯,An 均匀分布在 (0,N) 的区间,那么就可以估算出:
f(x)≈(n+1)⋅xN
为了方便计算,小 P 首先定义了比例系数 r=⌊Nn+1⌋,其中 ⌊ ⌋ 表示下取整,即 r 等于 N 除以 n+1 的商。进一步地,小 P 用 g(x)=⌊xr⌋ 表示自己估算出的 f(x) 的大小,这里同样使用了下取整来保证 g(x) 是一个整数。
显然,对于任意的询问 x∈[0,N),g(x) 和 f(x) 越接近则说明小 P 的估计越准确,后续进行线性查找的时间开销也越小。因此,小 P 用两者差的绝对值 |g(x)−f(x)| 来表示处理询问 x 时的误差。
为了整体评估小 P 同学提出的方法在序列 A 上的表现,试计算:
error(A)=∑i=0N−1|g(i)−f(i)|=|g(0)−f(0)|+⋯+|g(N−1)−f(N−1)|
输入格式
从标准输入读入数据。
输入的第一行包含空格分隔的两个正整数 n 和 N。
输入的第二行包含 n 个用空格分隔的整数 A1,A2,⋯,An。
注意 A0 固定为 0,因此输入数据中不包括 A0。
输出格式
输出到标准输出。
仅输出一个整数,表示 error(A) 的值。
样例1输入
3 10
2 5 8
Data
样例1输出
5
Data
样例1解释
A=[0,2,5,8]
r=⌊Nn+1⌋=⌊103+1⌋=2
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
f(i) | 0 | 0 | 1 | 1 | 1 | 2 | 2 | 2 | 3 | 3 |
g(i) | 0 | 0 | 1 | 1 | 2 | 2 | 3 | 3 | 4 | 4 |
|g(i)−f(i)| | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 |
样例2输入
9 10
1 2 3 4 5 6 7 8 9
Data
样例2输出
0
Data
样例3输入
2 10
1 3
Data
样例3输出
6
Data
样例3解释
A=[0,1,3]
r=⌊Nn+1⌋=⌊102+1⌋=3
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
f(i) | 0 | 1 | 1 | 2 | 2 | 2 | 2 | 2 | 2 | 2 |
g(i) | 0 | 0 | 0 | 1 | 1 | 1 | 2 | 2 | 2 | 3 |
|g(i)−f(i)| | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 |
子任务
70% 的测试数据满足 1≤n≤200 且 n<N≤1000;
全部的测试数据满足 1≤n≤105 且 n<N≤109。
提示
需要注意,输入数据 [A1⋯An] 并不一定均匀分布在 (0,N) 区间,因此总误差 error(A) 可能很大。
#include<iostream>
using namespace std;
int main() {
int n;
cin >> n;
int m;
cin >> m;
int* temp = new int[n];
for (int i = 0; i < n; i++) {
cin >> temp[i];
}
//这三个数代表了三个阶梯产生的值
//要注意的一点是在阶梯处会产生突变的变化
// int sum=0;
int cishu = 0;
int* temp2 = new int[m];
for (int i = 0; i < m; i++) {//检查出来问题出现在这里
if (i < temp[cishu]) {//错因是最后一位的无法检查与溢出的现象
temp2[i] = cishu;
}
else if (i >= temp[cishu] && i <= temp[n - 1]) {//这里不该拿cishu拿来比较 不然汇昌盛错误 并且这一点还是要突变的
cishu++;//直接突变
temp2[i] = cishu;//以上两个步骤是对结果进行的判断。如果出现阶梯就加上1
}
else if (i >= temp[cishu] && i > temp[n - 1]) {
temp2[i] = cishu;
}
}
int midd = m / (n + 1);
int sum2 = 0; //用来记录最终的 总和的变量 一般一道题要设计很多的边量 如果每个都不加以说明
//那么将是很难看懂的
// int cishu=-1;//因为这里从开始就视为 产生了突变
//每天的思路都死不一样的 发现今早起来才知道又有了其他的思路
//想到的四六数可以先把这个数组交流下俩再意义处理没有一位的变化
//接下来是建立数组的过程
//利用外常数变量来具体记录具体的位次的变化
//----------------------------------------------------
int temp3 = 0;
int shu = 0;
int* newarr = new int[m];
for (int i = 0; i < m; i++) {
if (temp3 % midd == 0&&temp3!=0) {
shu++;
}
newarr[i] = shu;
temp3++;
//这里是因为不会在第三个发生突变
}
shu = 0;
//-------------------------------利用两个变量记录并且登记处理具体过程的 并在中间进行记录的过程
for (int i = 0; i < m; i += 1) {
sum2 += abs(newarr[i] - temp2[i]);
}
cout << sum2 << endl;
return 0;
}
//debug 报告
//目前看来 在第一个数组是没有问题的
//发现了错误出现第55行 0也是满足了整除的关系