CodeForce-955C

本秂侑毒 提交于 2020-12-17 07:57:49

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\n",1+slove(l-1,r));
54         else printf("%lld\n",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\n",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)中第一个大于等于某个数字的元素地址。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!