https://www.acmicpc.net/problem/1016
1016번: 제곱 ㄴㄴ 수
첫째 줄에 min과 max가 주어진다. min은 1보다 크거나 같고, 1,000,000,000,000보다 작거나 같은 자연수이고, max는 min보다 크거나 같고, min+1,000,000보다 작거나 같은 자연수이다.
www.acmicpc.net
시간초과가 나서 되게 고민했던 문제.
#include <stdio.h>
#include <math.h>
#define ll long long int
bool chk[1000001] = {0};
int main(){
ll min,max;
ll sum = 0;
scanf("%lld %lld", &min, &max);
sum = max - min + 1;
for(ll i=2; i*i<=max; i++){
for(ll j=min/(i*i); i*i*j<=max; j++){
if(min <= i*i*j){
if(chk[i*i*j-min] == 0){
chk[i*i*j-min] = 1;
sum--;
}
}
}
}
printf("%lld", sum);
return 0;
}
1차로 고민했던 부분은 수의 범위가 너무 넓다는거다
그냥 O(n^2) 알고리즘으로 짜면 시간제한을 훌쩍 넘겨버린다.
그래서 백만번씩 돌면서 다 확인하려는 생각을 접고
min ~ max사이의 수 중 조건에 해당하는 만큼을 빼서 구하자 생각을 했다.
그래서 sum 변수를 만들어서 범위내에 해당하는 수의 개수를 구해줬고,
제곱수의 배수가 아닌 수들이 [제곱 ㄴㄴ 수] 이니까
먼저 제곱수를 구해주기 위해 첫번째 포문을 작성해주었다.
두번째 포문에서 2차로 고민하게 됬다.
for(ll j=min/(i*i)) i*i*j<=max; j++)
종료조건은 배수범위가 max를 넘어가면 종료시키니 그렇게 어렵지 않았지만,
j를 1부터 시작하니 시간초과가 일어나서 따로 최소범위를 지정해주어야 했다.
min ~ max 사이의 제곱 ㄴㄴ 수의 최소값을 k라 하자면,
min <= i*i*k 이므로
잘 이항해서 정리해줬다.
ceil 함수는 올림 함수인데, math.h를 포함해야지만이 사용 가능하다.
알아두면 좋을 것 같다.
if(min <= i*i*j){
바로 아랫줄에 얘가 있는 이유는 <=이기 때문에 살짝 불안해서 확인차 넣어줬다.
3번째로 고민했던 부분은 배수를 제거할때
2*2*4와
4*4같이 겹치는 부분이 생긴다는 점이었다.
배열을 만들어 확인하면 되겠지만, 배열을 만들기엔 너무 큰 범위이다.
문제에서 힌트를 얻었는데,
max의 값이 min + 1000000 이하라는 점이다.
즉 아무리 많아도 제곱 ㄴㄴ수들은 1000000개를
넘을 수 없으므로 1000001개의 배열을 만들어주었고,
min을 뺀 값을 인덱스로 사용했다.
그리고 sum에서 값을 하나씩 빼주는 방법으로 1차로 고민했던 방법을 구현해낼 수 있었다.