2024牛客暑期多校训练营9 I Interesting Numbers

原题链接:I-Interesting Numbers_2024牛客暑期多校训练营9 (nowcoder.com)

题目:

解题思路:

考察:高精度 数学性质

看了一下大家的题解好像有挺多种方法的,在这里仅提供自己的做题思路【因为别的我也看不懂┭┮﹏┭┮】

首先这里有几点开平方的数学性质,大家最好记一下

  • 平方根的性质

性质:一个数的平方根是唯一的(非负实数范围内),且平方根函数是单调增函数。

优化策略:

使用高效的平方根计算算法或库函数来计算给定数的平方根。
由于平方根函数是单调的,可以利用二分查找等算法来快速逼近精确值,特别是在需要高精度时。

  • 整数平方数的性质

性质:整数平方数的个位数只能是0, 1, 4, 5, 6, 9。

优化策略:

在遍历或搜索可能的完全平方数时,可以跳过不满足这一性质的数,从而减少计算量。

  •  区间内完全平方数的数量

性质:区间
[A, B]
[A,B]内的完全平方数数量等于该区间内最大完全平方数的平方根(向上取整)减去最小完全平方数的平方根(向下取整)。

优化策略:

直接计算区间上下限的平方根,并根据需要调整取整方向,以快速得到完全平方数的数量。
无需遍历区间内的每一个数来判断其是否为完全平方数。

  • 高效的数据结构和算法

优化策略:

使用适当的数据结构(如哈希表、平衡二叉搜索树等)来存储和查询已计算的完全平方数,以便在需要时快速检索。
利用并行计算技术(如多线程、多进程)来加速大规模计算,特别是在多核处理器上。

  • 数学公式和恒等式

性质:利用数学公式和恒等式(如平方差公式、完全平方公式等)来简化计算。

优化策略:

在计算过程中,尽量利用这些公式和恒等式来减少计算步骤和复杂度。
例如,在判断一个数是否为完全平方数时,可以尝试将其表示为两个相近整数的乘积,并检查这两个整数是否相等。

  • 缓存和预计算

优化策略:

对于经常需要查询的区间或范围内的完全平方数,可以预先计算并存储结果,以便在需要时快速检索。
使用缓存机制来存储和重用之前的计算结果,避免重复计算。

这题我的做题思路主要运用到这一点

 

  很明显根据这一点就可以很快求出区间内的完全平方数的个数,因为0也包括在内所以区间[0-x]的完全平方数量就位sqrt(x)+1,但是根据题目意思是要求出区间内符合平均分为两份,两份都是完全平方数的数量,再看数据范围是1e60,拆解开来仍然有1e30那么大。仅仅long long是不够的,需要用到__int128,__int128的数据范围是10的38次方。‌

  对于区间内的完全平方数数量,对于一个数是否符合条件,我们需要把它拆解为相同长度的l、r两个部分再分别判断,然后我们考虑组合数的思想,只要求出左边的个数乘上右边的个数即可。因为有区间范围的限制,所以我们可以暂时不考虑端点,先求中间的方便计算。

1.中间:

对于左半边部分,若不考虑端点那么完全平方数的区间就为【r-1,l+1】,然后根据之前开平方的性质+前缀和思想就可以快速求得这个区间范围内的数量。

右半边因为没有考虑端点,范围就是【0,9…9】(n/2个9)。同理可以求得个数

最后相乘就可以得到答案

2.端点

对于端点首先判断左半边是否符合条件,然后在计算就可以。这里不方便讲解大家自己看看代码多体会一下。

注意

  • cout不能输出__int128的数据范围,需要转成字符串或者数组一个个输出
  • 对于大数开平方,可以用二分法求
  • 字符串转数字好像没有直接的函数,需要自己手写
  • 感觉自己的方法还是很水,如果数据范围再大一点就不行了…┭┮﹏┭┮

源代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef __int128 ull;
map<string,int> mp;
void print(ull sum)
{
    vector<int> a;
    while(sum)
    {
        int x=sum%10;
        sum/=10;
        a.push_back(x);
    }
    for(int i=a.size()-1;i>=0;i--)
        cout<<a[i];
}
ull fun(ull n)
{
    ull l=0,r=1e15;
    
    while(l<r)
    {
        ull mid=r+l+1>>1;
        if(mid*mid<=n) l=mid;
        else r=mid-1;
    }
    return l;
}
ull to_ull(string x)
{
    ull ans=0;
    for(int i=0;i<x.size();i++)
    {
        ans=ans*10+(x[i]-'0');
    }
    return ans;
}
int main()
{
    int n; cin>>n;
    fun(n/2);
    string l,r;
    cin>>l>>r;
    ull sum=0;
    string l1=l.substr(0,n/2);
    string l2=l.substr(n/2,n/2);
    string r1=r.substr(0,n/2);
    string r2=r.substr(n/2,n/2);
//     先计算中间的
//     cout<<l1<<" "<<r1<<endl;
    string k="";
    for(int i=0;i<n/2;i++) k+='9';
//     cout<<kk<<endl;
    ull kk=to_ull(k);
    ull ans1=ull(fun(kk)+1);
   
    ull ans2=fun(to_ull(r1)-1)-fun(to_ull(l1));
    sum=ans2*ans1;
    ull ansl;
    if(to_ull(l2))
     ansl=fun(kk)-fun(to_ull(l2)-1);
    else 
    ansl=fun(kk)+1;

    ull ansr=fun(to_ull(r2))+1;

    if(to_ull(l1)==fun(to_ull(l1))*fun(to_ull(l1)))
        sum+=ansl;
     if(to_ull(r1)==fun(to_ull(r1))*fun(to_ull(r1)))
        sum+=ansr;
//     cout<<sum;
    print(sum);

    
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值