알고리즘/boj

BOJ 1016 제곱 ㄴㄴ 수

칼퇴시켜주세요 2023. 1. 17. 02:38
728x90

문제에서 어떤 두 정수 min과 max 사이에 수가 1보다 큰 제곱수로 나누어 떨어지지 않을때 제곱 ㄴㄴ 수 라고 정의를 하였다. 어떤 수가 제곱수로  나누어 떨어지지 않은것을 구하기 위해 우리는 에라토스테네스 방법을 응용할 수 있다.

 

처음 떠올린 방법은 부르트포스로 생각하였는데 제곱수가 될수 있는 n이 1000000, max-min이 1000000이므로 1000000*1000000 즉 1조가되어 시간초과가 발생한다.

 

기본적인 에라토스테네스는 소수를 구하기 위해 2,3,5,7의 배수를 모두 지우고 지워지지 않고 남은 수를 우리는 소수임을 알수 있다.

 

이를 응용하여 i*i (i>1) 가 max 보다 작을때 까지 i*i의 배수를 모두 지워주면 된다. 이때 시작지점은 i*i 가 min 보다는 커야하므로 min/(i*i)을 기준으로 계산하면 된다. 

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

long long arr[1000002];

int main()
{
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);

	memset(arr, 0, sizeof(arr));

	long long small, large;
	cin >> small >> large;

	long long cnt = 0;
	long long i = 2;

	while(i*i <= large){
		long long sNum = small / (i*i);
		if(small % (i * i) != 0)
		{
			sNum += 1;
		}
		while(i*i*sNum <= large)
		{
			arr[i*i*sNum - small] = 1;
			sNum++;
		}
		i++;
	}
	
	for(long long i = 0; i <= 1000002; i++)
	{
		if(arr[i] == 1)
		{
			cnt++;
		}
	}

	cout << large - small +1 - cnt;
}

 풀이는 다음과 같다. 시작 인덱스는 sNum으로 small이 min, large가 max에 해당된다.

long long sNum = small / (i*i);를 이용하여 시작 인덱스를 구하고 만약 min이 제곱수로 나누어지지 않는다면 시작 인덱스를 하나 증가시켜준다. 이후 (i*i)*sNum이 large 보다 작다면 sNum을 증가시켜 반복한다. 

 

마지막으로 제곱수의 배수를 arr에 표시해주고 최종적으로 arr에 1의 갯수를 구한후 전체에서 빼주면 된다.

반응형

'알고리즘 > boj' 카테고리의 다른 글

BOJ 1459 걷기  (0) 2023.01.17
BOJ 1911 흙길 보수하기  (2) 2023.01.17
BOJ 1439 뒤집기  (0) 2023.01.14
BOJ 1789 수들의 합  (0) 2023.01.13
BOJ 1114 통나무 자르기  (0) 2023.01.13