피사노 주기
https://www.acmicpc.net/problem/9471
| 시간 제한 | 메모리 제한 | 제출 | 정답 | 맞힌 사람 | 정답 비율 |
|---|---|---|---|---|---|
| 1 초 | 128 MB | 1367 | 963 | 826 | 73.422% |
문제
1960년, IBM의 직원 Donald Wall은 피보나치 수열을 m으로 나눈 나머지가 주기를 이룬다는 것을 증명했다.
예를 들어, 피보나치 수열의 처음 10개를 11로 나눈 예는 다음과 같다.
| n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|---|---|---|---|---|---|---|---|---|---|---|
| F(n) | 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 | 34 | 55 |
| F(n) mod 11 | 1 | 1 | 2 | 3 | 5 | 8 | 2 | 10 | 1 | 0 |
나머지를 이용해서 만든 수열은 주기가 나타날 수 있다.
k(m)을 반복하는 부분 수열의 길이라고 했을 때, k(11) = 10임을 알 수 있다.
Wall은 아래와 같은 여러 가지 성질도 증명했다.

m이 주어졌을 때, k(m)을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 테스트 케이스의 개수 P가 주어진다.
각 테스트 케이스는 N과 M으로 이루어져 있다.
N은 테스트 케이스의 번호이고, M은 문제에서 설명한 m이다.
출력
각 테스트 케이스마다 테스트 케이스 번호를 출력하고 k(M)을 출력한다.
제한

예제 입력 1
5 1 4 2 5 3 11 4 123456 5 987654
예제 출력 1
1 6 2 20 3 10 4 15456 5 332808
출처
ICPC > Regionals > North America > Greater New York Region > 2013 Greater New York Programming Contest D번
- 문제를 번역한 사람: baekjoon
알고리즘 분류
처음 문제를 확인하고 증명이 있어 이걸 이용하여 문제의 답을 찾는 것인가 생각했다.
해당 식들을 구현하면서 이건 아닌 것 같다고 생각했고 다른 블로그를 보면서
이러한 문제는 어떤 식으로 접근하는지 확인하였다.
포인트는 피사노 주기는 피보나치수열을 M으로 나눴을 때 이 나머지가 규칙성을 가지고 반복된다는 것
피보나치수열은 F(0) = 0, F(1) = 1 … 나눌 숫자 M >= 2 조건이므로
이후의 연산에서 나머지가 0, 1 순서로 나온다면 주기가 반복된다는 뜻이다.
피보나치수열을 M으로 나누어주고 나머지가 0 과 1이 순서대로 나올 때 까지 반복해주면서
주기를 카운트 하면 문제를 해결 할 수 있다.
참고 : https://en.wikipedia.org/wiki/Pisano_period <- 피사노의 주기 위키
통과된 코드
#include <iostream>
#include <list>
using namespace std;
list<pair<int, int>> tcList;
list<pair<int, int>> resultList;
// 문제의 입력을 처리하는 함수
void GetInput()
{
int N;
cin >> N;
pair<int, int> tempPair;
while (N-- > 0) {
cin >> tempPair.first >> tempPair.second;
tcList.push_back(tempPair);
}
}
// 피사노의 주기를 확인하는 함수
int PeriodOfPisano(int tc)
{
int a = 0, b = 1, cnt = 0, temp =0;
// 주기가 나올때까지 반복해준다.
while (true)
{
// 모듈러 산술 분배법칙
temp = ((a % tc) + (b % tc)) % tc;
a = b;
b = temp;
cnt++; // 주기가 얼마인지 체크
// 나머지로 나눈 값이 처음과 같은 0과 1의 순서로 나온다면, 다시 주기가 반복된다는 뜻
// (F2 = F1 + F0 , 나머지도 계속 반복을 하게 된다.)
if (a == 0 && b == 1) break;
}
return cnt; // 주기 리턴
}
int main()
{
GetInput(); // 입력 받기
// 테스트 케이스만큼 순회
for (auto iter = tcList.begin(); iter != tcList.end(); iter++) { // 결과값 출력
cout << iter->first << " " << PeriodOfPisano(iter->second) << "\n";
}
return 0;
}


![백준 17263번 (Sort 마스터 배지훈, C++) [BAEKJOON]](https://lycos7560.com/wp-content/uploads/boj-og.png)
![백준 4949번 (균형잡힌 세상, C++, Stack) / 추가 반례 [BAEKJOON]](https://lycos7560.com/wp-content/uploads/boj-og-1.png)
![백준 18352번 (특정 거리의 도시 찾기, C++, Dijkstra) [BAEKJOON]](https://lycos7560.com/wp-content/uploads/2022/10/boj-og-1-2048x1070-1-1024x535.png)