CSP202112-2序列查询新解java100分

发布于:2022-12-28 ⋅ 阅读:(326) ⋅ 点赞:(0)

问题描述

试题编号: 202112-2
试题名称: 序列查询新解
时间限制: 1.0s
内存限制: 512.0MB
问题描述:

题目背景

上一题“序列查询”中说道:
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(log⁡n) 的时间复杂度内轻松解决。但在 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) 可能很大。

代码

import java.io.*;
import java.util.*;
public class Main {
    public static void main(String[] args)throws Exception {
        BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
        String[] s=bf.readLine().split(" ");
        int n=Integer.valueOf(s[0]),N=Integer.valueOf(s[1]),i;
        long error=0;
        s=bf.readLine().split(" ");
        int[] a=new int[n+1];
        a[0]=0;
        int r=N/(n+1),j=0,k=0,f=0,g=0;
        for(i=1;i<n+1;i++){
            a[i]=Integer.parseInt(s[i-1]);
            f=i-1;
            for(k=a[i-1];k<a[i];){
               if(k%r==0&&k+r<a[i]){
                    g=k/r;
                    error+=r*Math.abs(f-g);
                    k+=r;
                }
                else{
                    error+=Math.abs(f-k/r);
                    k++;
                }
            }
        }
        for(k=a[n];k<N;k++){
            error+=Math.abs(n-k/r);  
        }
      
        System.out.print(error);
    }
}

优化要点:

1.无须采用搜索或者单独遍历,fx的规律使得我们可以变读入边处理,使得循环次数减少

2.gx是一个周期为r的循环递增函数,对此我们可以以此计算出在该周期内值的乘积而不是逐个遍历相加,这可以进一步节省时间

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到