题解:[春季测试 2023] 幂次
给定 \(n,k\) ,求有多少个整数 \(i \in [1,n]\) ,满足 \(i=a^b (a,b \in N^+,b \geq k)\)
算法一
\(k \ge 3:\) 发现只需要筛到1e6就没有贡献了,加上 \(set\) 暴力判重即可。
\(k=2:\) 发现有 \(\sqrt{n}\) 个完全平方数,考虑如何避免算重它们。考虑完全平方数的性质。发现有且既有下面两种数是完全平方数:
- 一个完全平方数的任意次幂。
- 任意一个数的偶次幂。
还是用 \(set\) 判重,加上如上两点判断即可。
最坏时间复杂度为 \(O(10^6 \times log_2^{10^6})\),注意开平方根要用sqrtl
或者手写二分
#include<bits/stdc++.h>
#define F(i,l,r) for(ll i=(l);i<=(r);++i)
#define G(i,r,l) for(ll i=(r);i>=(l);--i)
#define ll long long
using namespace std;
set<ll> s;
ll n,k,x,cf;
//inline ll sqrt(ll x){
// ll l=0,r=1000000010ll,mid;
// while(l+1<r){
// mid=(l+r)>>1;
// if(mid*mid>x) r=mid;
// else l=mid;
// } return l;
int main(){
scanf("%lld %lld",&n,&k);
if(k==1) return printf("%lld",n),0;
else if(k>2){
F(i,2,n){
x=i*i*i; if(x>n) break;
F(j,0,60){
if(j+3>=k) s.emplace(x);
if(x>n/i) break;
x*=i;
}
}
printf("%lld",s.size()+1);
}
else{
ll ans=__builtin_sqrt(n);//要么用sqrtl,要么手写一个longlong的开根(最后一组数据卡精度)
F(i,2,n){
if(i*i*i>n) break;
ll tmp=__builtin_sqrt(i); if(tmp*tmp==i) continue;
x=i*i,cf=2;
do{
++cf,x*=i;
if((cf&1) && s.find(x)==s.end()) s.emplace(x);
}while(x<=n/i);
}
ans+=s.size();
printf("%lld",ans);
}
return 0;
}
算法二
我们考虑换一种判重方法:
1.对于 \(k \ge 3\) , 若 \(a^x=b^y\), 则有 \(a^x=(a^k)^y\), 那么在以 \(a\) 为底数时就一定能把 以 \(b\) 为底的情况给筛掉,所以我们记 \(vis_i\) 表示 底数 \(i\) 有没有被筛掉,然后就可以愉快地筛起来了。
vis只需要开到 1e6 .
2.对于 \(k=2\), 很明显开不下 vis[1e9]
, 所以再单独特判一下每个数是不是完全平方数即可。最后加 \(\sqrt{n}\) 个完全平方数,并减去重复的。
时间复杂度近似 \(\sqrt{n}.\)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+2;
int n,k,K,m,same=0,sq,ans=0;
bitset<N> b;
int qp(int x,int y){
int res=1;
while(y){ if(y&1){ if(res>n/x) return n+1; res=res*x; }y>>=1,x*=x; }
return res;
}
signed main(){
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin>>n>>K; if(K==1) return cout<<n,0;
sq=__builtin_sqrtl(n),k=max(K,3ll);
int l=0,r=1e6+2,mid;
while(l+1<r) mid=(l+r)>>1, (qp(mid,k)<=n) ? l=mid : r=mid;
for(int i=2;i<=l;++i){
if(b[i]) continue;
int z=i,o=0;
while(z<=n){
o++;
if(z<=l) b[z]=1;//标记
if(o>=k) ans++;//累计答案
if((int)sqrt((int)z)*(int)sqrt((int)z)==(int)z&&o>2) same++;//容斥(去重)
if(z>n/i) break;//判超界
z*=i;
}
}
if(K==2) ans=ans+sq-1-same;
cout<<ans+1;
return 0;
}
标签:return,幂次,int,题解,ll,mid,sqrt,res,2023
From: https://www.cnblogs.com/superl61/p/17827092.html