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

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

  • 10
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值