原题链接: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);
}