题目翻译
题目大意
我们规定对于两个不同的数字
a
,
b
a,b
a,b ,如果
gcd
(
a
,
b
)
,
a
gcd
(
a
,
b
)
,
b
gcd
(
a
,
b
)
\gcd(a,b),\frac{a}{\gcd(a,b)},\frac{b}{\gcd(a,b)}
gcd(a,b),gcd(a,b)a,gcd(a,b)b可以构成一个三角形,那么
a
,
b
a,b
a,b 便是一组好朋友.
我们进一步规定,如果在一个集合中中,有一数字
K
K
K和这个集合中任意一个数字都不是朋友,那么数字
K
K
K 就是一个孤独数字.
给定一个包含所有
1
1
1 到
n
n
n 整数的集合,求在该集合中有多少数字是孤独数字.
输入输出格式
第一行输入一个整数
T
T
T 表示有
T
T
T 组数据.
接下来一行
T
T
T 个数字,其中第
i
i
i 个数字为
n
i
n_{i}
ni,表示你应解决当
n
=
n
i
n=n_{i}
n=ni时的问题.
你应输出
T
T
T 行,第
i
i
i 行为当
n
=
n
i
n=n_{i}
n=ni时孤独数字的数量.
样例解释
当
n
=
1
n=1
n=1 时,
1
1
1 是集合中唯一的数字,因此它是孤独数字.
当
n
=
5
n=5
n=5 时, 孤独数字为
1
,
3
,
5
1,3,5
1,3,5
当
n
=
10
n=10
n=10 时, 孤独数字为
1
,
5
,
7
1,5,7
1,5,7
解析
首先易得暴力是
O
(
∑
i
=
1
T
n
i
2
)
O(\sum_{i=1}^{T}{n_{i}^{2}})
O(∑i=1Tni2)的时间来完成的(不计算gcd的时间)
5分钟就可以码出来:(这一份是输出有哪些的
#include<bits/stdc++.h>
#define forr(x,y,z) for(int x=y;x<=z;x++)
using namespace std;
int N,T,g;
int gcd(int a,int b){return b==0?a:gcd(b,a%b);}//gcd
bool ch(int a,int b,int c){return (a>b&&a>c)?(b+c>a):((b>c)?(c+a>b):(a+b>c));}//判断△
bool ans[100039];//桶
int main(){
scanf("%d",&T);
while(T--){
memset(ans,0,sizeof(ans));scanf("%d",&N);
forr(i,1,N)forr(j,1,N){g=gcd(i,j);if(i!=j&&ch(i/g,j/g,g))ans[i]=ans[j]=1;}
for(int i=1;i<=N;i++)if(!ans[i])printf("%d ",i);printf("\n");
}
}
输入 | 输出 |
---|---|
1 | 1 |
2 | 1 2 |
3 | 1 2 3 |
4 | 1 3 |
5-6 | 1 3 5 |
7-8 | 1 3 5 7 |
9-10 | 1 5 7 |
11-12 | 1 5 7 11 |
13-16 | 1 5 7 11 13 |
17-18 | 1 5 7 11 13 17 |
19-22 | 1 5 7 11 13 17 19 |
23-24 | 1 5 7 11 13 17 19 23 |
25-28 | 1 7 11 13 17 19 23 |
… … …… …… | … … …… …… |
可以发现:
- 除了 1 1 1之外,其他数都是质数
- 每到(或大于)一个质数的平方,这个质数就不在答案里(例如4,9,25)
总结一下,答案是大于
n
i
\sqrt{n_i}
ni,小于
n
i
n_i
ni的质数的个数
复杂度:
O
(
∑
i
=
1
T
{
∑
j
=
n
i
n
i
j
}
)
O\left(\sum_{i=1}^{T}\left\{{\sum_{j=\sqrt{n_i}}^{n_i}{\sqrt{j}}}\right\}\right)
O⎝⎛i=1∑T⎩⎨⎧j=ni∑nij⎭⎬⎫⎠⎞
优化
给质数一个预处理:
得:
O
(
∑
i
=
1
T
{
n
i
−
n
i
}
+
∑
i
=
1
1
e
6
i
)
O\left(\sum_{i=1}^{T}\left\{n_i-\sqrt{n_i}\right\}+\sum_{i=1}^{1e6}{\sqrt{i}}\right)
O(i=1∑T{ni−ni}+i=1∑1e6i)
优化*2
再来一点前缀和
O
(
1
e
7
(
近
似
)
+
T
)
O\left(1e7(近似)+T\right)
O(1e7(近似)+T)
代码
#include<bits/stdc++.h>
using namespace std;
int N,M,ans,add[1000039];
bool z[1000039];
int main(){
memset(z,1,sizeof(z));
for(int i=2;i<=1e6;i++){
if(z[i]){
int k=2*i;
while(k<=1e6){
z[k]=0,k+=i;
}
}
add[i]=add[i-1]+z[i];
}
scanf("%d",&N);
while(N--){
scanf("%d",&M);ans=0;
printf("%d\n",add[M]-add[(int)sqrt(M)]+1);
}
return 0;
}