[春季测试 2023] 幂次

题目描述

小 Ω 在小学数学课上学到了“幂次”的概念:a,bN+,定义 abba 相乘。

她很好奇有多少正整数可以被表示为上述 ab 的形式?由于所有正整数 mN+ 总是可以被表示为 m1 的形式,因此她要求上述的表示中,必须有 bk,其中 k 是她事先选取好的一个正整数。

因此她想知道在 1n 中,有多少正整数 x 可以被表示为 x=ab 的形式,其中 a,b 都是正整数,且 bk

输入格式

第一行包含两个正整数 n,k,意义如上所述。

输出格式

输出一行包含一个非负整数表示对应的答案。

样例 #1

样例输入 #1

99 1

样例输出 #1

99

样例 #2

样例输入 #2

99 3

样例输出 #2

7

样例 #3

样例输入 #3

99 2

样例输出 #3

12

提示

【样例 2 解释】

以下是全部 7 组符合题意的正整数及对应的一种合法的表示方法。

1=13,8=23,16=24,27=33,32=25,64=43,81=34

注意某些正整数可能有多种合法的表示方法,例如 64 还可以表示为 64=26

但根据题意,同一个数的不同的合法表示方法只会被计入一次。

【样例 3 解释】

以下是全部 12 组符合题意的正整数及对应的一种合法的表示方法。

1=12,4=22,8=23,9=32,16=42,25=52,27=33,32=25,36=62,49=72,64=82,81=92

【样例 4】

见选手目录下的 power/power4.in 与 power/power4.ans。

【样例 5】

见选手目录下的 power/power5.in 与 power/power5.ans。

【样例 6】

见选手目录下的 power/power6.in 与 power/power6.ans。

【数据范围】

对于所有数据,保证 1n10181k100

测试点编号 n k
1 102 =1
2 102 2
3 104 3
4 104 2
5 106 3
6 106 2
7 108 3
8 108 2
9 1010 3
10 1010 2
11 1012 3
12 1012 2
13 1014 3
14 1014 2
15 1016 3
16 1016 2
17 1018 3
18 1018 2
19 1018 2
20 1018 2

题解

首先看到这种ab这种情况,我就知道能枚举的数量有限,我算了一下,x3=1e18时,x的值为251188,所以我先尝试写了一个暴力,那就是枚举指数,枚举底数,用快速幂算一下ab的值,同时我用标记数组记录x是否被计算过,这样的时间复杂度也不会很大

上述算法有个问题需要注意,标记数组能算的非常有限,所以只能得到40分的好成绩,所以我需要改进每个数被记录的方式。我用标记数组其实有点儿浪费,因为里面有很多数都没有被用过,比如说2,3,5,6,7等数,所以我想到我用map来记录,修改之后,成绩就变成了75了,好,非常棒

那么剩下的没得分的原因是什么呢?因为刚才的考虑是指数至少是3,如果是2的话,如果n=1e18,那么至少需要枚举到1e9,这样的话,肯定会T,但是我要记录1到1e18里有多少个完全平方数啊?个数非常好记录,同时我要知道他被用过,按照刚才的思路,得把它记录在map中,这样的话,时间复杂度就上去了,这就出现了矛盾

转念一想,我其实没有必要记录到map中,如果在指数大于等于3的时候,我们发现一个数符合条件,我们只需要判断一下,他是否是完全平方数即可,这样时间复杂度就降下来了,嗯,非常棒

另一方面,当指数为1时,肯定n以内的所有数都符合要求,所以个数就是n,所以直接输出,单独处理

有了这样的完整思路,我把代码交上去,发现95分,原因在这里,请看下图

点击查看代码
#include<bits/stdc++.h>
using namespace std;
long long n,ans;
int k;
unordered_map<long long,bool>p;
long long ksm(long long x,int y){
long long ans=1;
while(y){
if(y&1) ans=ans*x;
x=x*x;
y=(y>>1);
}
return ans;
}
bool pd(long long x){
long long k=sqrt(x);
if(k*k==x) return true;
else return false;
}
int main(){
scanf("%lld%d",&n,&k);
if(k==1){
printf("%lld",n);
return 0;
}
if(k==2){
long long mx=sqrtl(n);
ans+=mx-1;
}
for(int i=3;;i++){
if(ksm(2,i)>n) break;
long long x=pow(n,1.0/i);
for(long long j=2;j<=x;j++){
long long d=ksm(j,i);
if(d<=n){
if(p[d]==false) {
if(pd(d)==true&&k==2) continue;
p[d]=true;
ans++;
}
}
else break;
}
}
printf("%lld",ans+1);
return 0;
}
posted @   xinyimama  阅读(69)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示