Codefoces 955C 题解

题意简述

多组数据。每次给定 l l l, r r r( l , r l,r l,r &lt; = 1 e 18 &lt;=1e18 <=1e18),求 l l l~ r r r中有多少 x x x满足 x = a p x=a^p x=ap,其中 a a a p p p是两个整数满足 a &gt; 0 , p &gt; 1 a&gt;0,p&gt;1 a>0,p>1

数据

输入:
6
1 4
9 9
5 7
12 29
137 591
1 1000000

输出:
2
1
0
3
17
1111

思路

会发现,当 p &gt; = 3 p&gt;=3 p>=3时,使得 0 &lt; = a p &lt; = 1 e 18 0&lt;=a^p&lt;=1e18 0<=ap<=1e18 a a a并不多, p = 3 p=3 p=3大概有 1 e 6 1e6 1e6,然后后面更少。经计算,总共的个数(去重后)是 1003332 1003332 1003332,就比 1 e 6 1e6 1e6多一点。考虑暴力存下,排序去重后存着。 p &gt; = 3 p&gt;=3 p>=3的情况只要 u p p e r b o u n d − l o w e r b o u n d upperbound-lowerbound upperboundlowerbound即可(lower_bound和upper_bound返回的都是指针,如果相减就珂以得到中间长度了。不用 + 1 +1 +1的原因是upper_bound找到的那个不珂以取)。记为 a 1 a_1 a1

那如何处理 p = 2 p=2 p=2的情况?

相当于求有多少 a a a满足 l &lt; = a 2 &lt; = r l&lt;=a^2&lt;=r l<=a2<=r

我们会发现,如果设 S ( n ) S(n) S(n)为满足 1 &lt; = a 2 &lt; = n 1&lt;=a^2&lt;=n 1<=a2<=n a a a个数,则我们要求的就是 S ( r ) − S ( l − 1 ) S(r)-S(l-1) S(r)S(l1)

由数学常识得, S ( n ) = n S(n)=\sqrt{n} S(n)=n

故我们要求的就是 r − l − 1 \sqrt{r}-\sqrt{l-1} r l1 。记为 a 2 a_2 a2

然后只要把 a 1 a_1 a1 a 2 a_2 a2加一起即可。

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int Max=1000000ll*1000000ll*1000000ll;//1e18

int Root(int x)
{
    return (int)sqrt(x*1.0+0.5);
}
vector<int>tmp;
void Build()
{
    for(int i=2;i<=1000000;i++)//枚举底数
    {
        int s=i*i*i;//至少三次方
        while(s<=Max)
        {
            int rs=Root(s);
            if (rs*rs<s) tmp.push_back(s);
            //这个就是判s是否是平方数,为了避免和后面考虑p=2的情况重合

            if (s>Max/i) break;//如果爆了赶快退出
            else s*=i;//没爆继续乘
        }
    }
    sort(tmp.begin(),tmp.end());
    tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end());//排序去重,方便lower_bound和upper_bound
    //printf("%d\n",tmp.size());
    //=1003332,如果要自己写(即不完全Copy我的),珂以先对一下这个答案
}

int l,r;
void Solve()
{
    int a1=upper_bound(tmp.begin(),tmp.end(),r)-lower_bound(tmp.begin(),tmp.end(),l);
    int a2=Root(r)-Root(l-1);
    printf("%I64d\n",a1+a2);//上面说的a1和a2
}

main()
{
    Build();
    int T;scanf("%I64d",&T);
    while(T--)
    {
        scanf("%I64d%I64d",&l,&r);
        Solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值