2020 CCPC 秦皇岛站 Promble G. Good number
题意:
一个正整数x,当且仅当x开k次方(向下取整)整除x时,即x/x^1/k,x为一个good number。现给定一个整数n,求1到n之间有多少个good number?
分析:
这道题运用到了分块,
通过判断得出,k分为三种情况:
1.当k=1时,即x除以x本身,1到n间所有数字都能满足good number,输出n
2.当k<=31时(以k=2为例)
我们可以把x分为多个块,划分依据为x开k次方后向下取整的结果是否相同
当x∈[1,3]时,开k次方后向下取整,结果都为1,能被1整除的数:1,2,3 即数目为:3
当x∈[4,8]时,开k次方后向下取整,结果都为2,能被2整除的数:4,6,8 即数目为3
当x∈[9,15]时,开k次方后向下取整,结果都为3,能被3整除的数:9,12,15 即数目为3
当x∈[16,24]时,开k次方后向下取整,结果都为4,能被4整除的数:16,20,24 即数目为3
当x∈[25,35]时,开k次方后向下取整,结果都为5,能被5整除的数:25,30,35 即数目为3
……
我们可以发现规律,
数目=(块的最后一个数-块的第一个数)/x开方取整后的数+1
3.当k>32时, 因为232=4294967296>109, x开k次方后为1,所以1到n间所有数字都满足good number,输出n
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
//快速幂
ll fun(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1)
ans*=a;
a*=a;
b>>=1;
}
return ans;
}
int main()
{
int t,arr,brr;
ll n,k;
cin>>t;
for(int i=1;i<=t;i++)
{
ll ans=0;
scanf("%lld%lld",&n,&k);
if(k==1||k>=31)//当k=1,或k>=31时,开k次方后只能为1,所有数都可以整除
{
printf("Case #%d: %lld\n",i,n);
continue;
}
else
{
for(int j=1;fun(j,k)<=n;j++)//j:x开方向下取整后的数
{
arr=fun(j,k);//次方数,即每个块的第一个数
brr=min(n,fun(j+1,k));//判断块是否越界,brr:下一个块的第一个数
ans+=(brr-1-arr)/j+1;//brr-1:每块的最后一个数,j~arr间的数为底数相同的块
}
printf("Case #%d: %lld\n",i,ans);
}
}
return 0;
}
结果: