2020 CCPC 秦皇岛站 Promble G. Good number

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;
}

结果:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_45830165/article/details/109245999