[思维]CF1423K

[思维]CF1423K Lonely Numbers

题目翻译

题目大意
我们规定对于两个不同的数字 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");
	}
}
输入输出
11
21 2
31 2 3
41 3
5-61 3 5
7-81 3 5 7
9-101 5 7
11-121 5 7 11
13-161 5 7 11 13
17-181 5 7 11 13 17
19-221 5 7 11 13 17 19
23-241 5 7 11 13 17 19 23
25-281 7 11 13 17 19 23
… … …… … … ……

可以发现:

  1. 除了 1 1 1之外,其他数都是质数
  2. 每到(或大于)一个质数的平方,这个质数就不在答案里(例如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) Oi=1Tj=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=1T{nini }+i=11e6i )
优化*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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值