CSP202112-2序列查询新解java100分

该博客讨论了一种序列查询的新方法,由小P同学提出。这种方法基于序列中整数的分布进行估算,以线性查找替代二分查找。通过计算序列中每个元素与估算值之间的误差之和`error(A)`来评估方法的准确性。博客提供了不同样例的输入和输出,并指出在某些情况下,此方法可能比二分搜索更有效。优化点包括减少循环次数和利用周期性计算误差。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述

试题编号: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

i0123456789
f(i)0011122233
g(i)0011223344
|g(i)−f(i)|0000101111

样例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

i0123456789
f(i)0112222222
g(i)0001112223
|g(i)−f(i)|0111110001

子任务

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的循环递增函数,对此我们可以以此计算出在该周期内值的乘积而不是逐个遍历相加,这可以进一步节省时间

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值