955C. Sad powers(思维)
题目链接:传送门
思路:对于1到n的满足 a b = x a^b=x ab=x的数 x x x(幂次 b b b>1)。
我们可以将之分为两种,一种是 b = 2 b=2 b=2,另一种 b > 2 b>2 b>2且不是完全平方数的个数。
对于第一种情况( b = = 2 b==2 b==2),即我们计算完全平方数的个数,这部我们可以二分 O ( l o g n ) O(logn) O(logn)
对于第二种情况( b > 2 b>2 b>2),因为 b = 3 b=3 b=3时这样数最多只有 1 0 18 / 3 = 1 0 6 10^{18/3}=10^6 1018/3=106种, b > 3 b>3 b>3的则更少,所以这种情况的数我们可以暴力预处理。
但是第二种情况可能有重复计算,我们去重即可。但是第一种情况和第二种情况可能也有重复计算,我们再筛去第二种情况的完全平方数。
这样我们计算 f ( n ) f(n) f(n)即小于等于 n n n,满足 a b = x a^b=x ab=x的数, O ( l o g n ) O(logn) O(logn)计算出第一种情况即可, O ( n ) − O ( l o g 1 e 6 ) O(n)-O(log1e6) O(n)−O(log1e6)计算第二种情况的即可。
代码:
#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
vector<ll> _o,o;
ll root(ll x)
{
ll ans=0;
ll l=1,r=1e9;
while(l<=r)
{
ll m=(l+r)/2;
if(m*m<=x){
ans=m;
l=m+1;
}
else r=m-1;
}
return ans;
}
ll f(ll n)
{
int ans=upper_bound(o.begin(),o.end(),n)-o.begin();
return ans+root(n);
}
void init()
{
ll U=1e18;
for(ll i=2;i<=1000000;++i)
{
for(ll cur=i*i*i;cur<=U;cur*=i)
{
_o.push_back(cur);
if(cur> U/i) break;
}
}
sort(_o.begin(),_o.end());
_o.erase(unique(_o.begin(),_o.end()),_o.end());
for(ll &v:_o)
{
ll r=root(v);
if(r*r==v) continue;
o.push_back(v);
}
}
ll solve(ll l,ll r)
{
return f(r)-f(l-1);
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0);
init();
int t;
cin>>t;
while(t--)
{
ll l,r;
cin>>l>>r;
cout<<solve(l,r)<<endl;
}
}
这里提供一种容斥,但是超时的思路。
满足 a b = x a^b=x ab=x,的数的 b b b 无非是 2 , 3 , . . . 63 2,3,...63 2,3,...63,又因为所有合数(如6=2*3),所以幂次为合数的数也是幂次为素数的数。
所以我们我们只需 b b b 取 2 , 3 , 5 , 7 , 11... 2,3,5,7,11... 2,3,5,7,11...即可。但是像幂次为 2 , 3 2,3 2,3这样有重复计算,即幂次为 6 6 6的,所以这里就用到容斥了,我们容斥的是幂次为 2 , 3 , 5 , 7 , 11 , . . . 2,3,5,7,11,... 2,3,5,7,11,...的数的个数。
对于 b > 63 b > 63 b>63的我们不予处理,因为满足幂次为 > 63 >63 >63的数为0个,预处理后发现容斥里面的幂次的个数有 38 38 38个。
但是要怎么计算1~n内幂次为 b b b次的数的个数呢?
用 p o w ( n , 1 / b ) pow(n,1/b) pow(n,1/b)即可,不过因为有误差,所以我们需要检测前面或后面的是否满足 a b ≤ b a^b\le b ab≤b。
#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int num[1005],tot;
int rc[maxn],cnt;
int top;
vector<int> prime;
int book[1005];
bool check(ll a,ll b,ll n)//return a^b<=n
{
ll ans=1;
for(ll i=1; i<b; ++i)
{
n/=a;
}
if(a <= n)return true;
else return false;
}
void init()
{
book[1]=1;
for(int i=2; i<=63; ++i)
{
if(!book[i])
{
prime.push_back(i);
for(int j=i*i; j<=63; j+=i) book[j]=1;
}
}
tot=0;
for(int p:prime)
num[tot++]=p;
cnt=0;
rc[cnt++]=-1;
for(int i=0; i<tot; ++i)
{
int k=cnt;
for(int j=0; j<k; ++j)
{
ll m=num[i]*(rc[j] < 0? -1 *rc[j]:rc[j]);
if(m > 63)
continue;
rc[cnt++]=m*(rc[j]<0?1:-1);
}
}
}
ll getnum(ll n,ll k)//take maximal a of a^k<=n
{
ll r=pow(1.0*n,1.0/k);
while(!check(r,k,n)) --r;
while(check(r+1,k,n)) ++r;
return r-1;
}
ll getcnt(ll n)
{
if(n==0) return 0;
if(n < 4) return 1;
int top=0;
ll m=n;
ll ans=1;
for(int i=1; i<cnt; ++i)
{
if(rc[i] < 0)
ans-=getnum(n,rc[i]*-1);
else
ans+=getnum(n,rc[i]);
}
return ans;
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
init();
ll l,r,t;
cin>>t;
while(t--)
{
cin>>l>>r;
cout<<getcnt(r)-getcnt(l-1)<<endl;
}
return 0;
}