C. Sad powers
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output
You're given Q queries of the form (L, R).
For each query you have to find the number of such x that L ≤ x ≤ R and there exist integer numbers a > 0, p > 1 such that x = ap.
Input
The first line contains the number of queries Q (1 ≤ Q ≤ 105).
The next Q lines contains two integers L, R each (1 ≤ L ≤ R ≤ 1018).
Output
Output Q lines — the answers to the queries.
Example
input
Copy
6
1 4
9 9
5 7
12 29
137 591
1 1000000
output
2
1
0
3
17
1111
Note
In query one the suitable numbers are 1 and 4.
题目大意:
找到L—R之间的所有可以由x的p次幂所表示的数
思路:
虽然暴力是个好东西,但wa题也是很容易。
最初的想法是:遍历每个数字的每次幂,但很明显是行不通的。有趣的是,走不通的路,也还要试一下看看能过几个样例。然后wa在了第三个样例上。。。有兴趣可以看一眼真·暴力代码。如下:
1 #include<stdio.h> 2 #include<string.h> 3 int vis[100000000]; 4 5 typedef long long ll; 6 7 ll push_pow(ll a,ll b) 8 { 9 ll ans=1;ll base=a; 10 while(b) 11 { 12 if(b%2) ans*=base; 13 base*=base; 14 b/=2; 15 } 16 return ans; 17 } 18 19 ll slove(ll l,ll r) 20 { 21 memset(vis,0,sizeof(vis)); 22 int flag=1;ll ans1=0,ans2=0; 23 for(int i=2;flag;i++) 24 { 25 for(int j=2;;j++) 26 { 27 if(l>=push_pow(i,j)&&!vis[push_pow(i,j)]) 28 { 29 ans1++; 30 } 31 if(r>=push_pow(i,j)&&!vis[push_pow(i,j)]) 32 { 33 vis[push_pow(i,j)]=1;ans2++; 34 } 35 else if(j==2&&!vis[push_pow(i,j)]) 36 { 37 flag=0; 38 break; 39 } 40 else break; 41 } 42 } 43 return ans2-ans1; 44 } 45 46 int main() 47 { 48 ll T,l,r; 49 scanf("%lld",&T); 50 for(int o=1;o<=T;o++) 51 { 52 scanf("%lld%lld",&l,&r); 53 if(l==1) printf("%lld ",1+slove(l-1,r)); 54 else printf("%lld ",slove(l-1,r)); 55 } 56 return 0; 57 }
暴力不是ac的秘诀,那么这道题该怎么解决呢?在思考无果的情况下,百度。。。
事实它告诉我,这题不是我原先可以解决的。于是乎,涨知识了。
言归正传,我们可以把问题分割,分成两部分,一部分是二次方,一部分是p次方(p>2)。
二次方不必说,二分查找很容易解决问题。
主要是p次方,当p=3时,1e18开三次根号1e6,复杂度在可接受范围内。
而当p>3时,由于幂函数的增幅极大,所以到大于1e18花费的时间是很少的,以最多次幂的2举例,1e18没有爆longlong,那么次数少于64,很明显,花费也是可以接受的。
AC代码如下:
1 #include<stdio.h> 2 #include<vector> 3 #include<algorithm> 4 using namespace std; 5 6 typedef long long ll; 7 vector<ll>q; 8 9 int root(ll x) //找根 10 { 11 ll left=0;ll right=1e9; //left必须从0开始,因为调用函数时会有x==0的情况 12 ll mid,ans; 13 while(left<=right) 14 { 15 mid=(left+right)>>1; 16 if(mid*mid<=x) 17 { 18 ans=mid; 19 left=mid+1; 20 } 21 else 22 { 23 right=mid-1; 24 } 25 } 26 return ans; 27 } 28 29 void init() //p>2时,可以由x的p次方表示的数 30 { 31 q.clear(); 32 for(ll i=2;i<=1e6;i++) 33 { 34 double t=1.0*i*i*i; 35 ll s=i*i*i; 36 while(t<2e18) 37 { 38 ll root_s=root(s); 39 if(root_s*root_s<s) 40 q.push_back(s); 41 t*=i;s*=i; 42 } 43 } 44 sort(q.begin(),q.end()); //下面会解释 45 unique(q.begin(),q.end()); 46 } 47 48 int main() 49 { 50 init(); 51 ll T; 52 ll l,r; 53 scanf("%lld",&T); 54 for(int i=1;i<=T;i++) 55 { 56 scanf("%lld%lld",&l,&r); 57 ll ans1=upper_bound(q.begin(),q.end(),r)-lower_bound(q.begin(),q.end(),l);//下面会解释 58 ll ans2=root(r)-root(l-1); 59 printf("%lld ",ans1+ans2); 60 } 61 return 0; 62 }
unique()函数
sort(q.begin(),q.end()); unique(q.begin(),q.end());
独一无二的,该函数的作用是把相邻的两个相同的数字中消去一个,让我想起了天梯赛的1-8(AI牛批!
相邻的两个相同的数字,那么sort()函数的作用就体现在为unique()函数服务上了。
upper_bound()与lower_bound()函数
ll ans1=upper_bound(q.begin(),q.end(),r)-lower_bound(q.begin(),q.end(),l);
upper_bound()作用是返回[l,r)中第一个大于某个数字的元素地址,lower_bound()的作用是返回[l,r)中第一个大于等于某个数字的元素地址。