종말의 시작
주제 배경
드디어 출발점이 끝났고,
드디어 기간이 쓰여지고,
드디어 작별을 고
하고, 드디어 시작했던 곳으로 돌아왔습니다
...
각 OIer의 경쟁 경력은 항상 NOIp로 시작하고 대부분은 환생이 계속 진행되는 것처럼 NOIp로 끝납니다.
이 NOIp가 시작점이라면 OI 경력이 화려한 여름이 되기를 바랍니다.
이 NOIp가 끝이라면 OI 메모리가 별처럼 빛나길 바랍니다.
LA에서 플레이하는 것이 이번이 마지막일 수도 있고 아닐 수도 있습니다.
아무튼 다음주도 잘 부탁드립니다.
물론 이 질문은 환생과도 관련이 있다.
주제 설명
잘 알려진 피보나치 수열 fib (n ) \mathrm{fib}(n)fib ( n ) 은 다음과 같이 계산됩니다.
즉 0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 ⋯ 0, 1, 1, 2, 3, 5, 8, 13 \cdots0 ,1 ,1 ,2 ,3 ,5 ,8 ,13⋯ , 각 항은 이전 두 항의 합입니다.
Little F는 피보나치 수열의 각 항목이 임의로 1 1 보다 큰 경우1 양의 정수MMM 이 모듈로이면 시퀀스가 사이클을 생성합니다.
물론 작은 F는 ( fib (n − 1 ) mod M \mathrm{fib}(n - 1) \bmod M 때문에 금방 이해했습니다.섬유 ( n-1 )모드M ) 和 (fib (n − 2 ) mod M ) \mathrm{fib}(n - 2) \bmod M)섬유 ( n-2 )모드M ) 은 최대M 2 M^2중2 값이므로M 2 M^2중2회 계산 후 주기가 있어야 합니다
더 일반적으로, 계수 MM이 무엇이든 관계없이M , 최종 모듈러스MMM 아래의 피보나치 수열은0 , 1 , ⋯ , 0 , 1 , ⋯ 0, 1, \cdots, 0, 1, \cdots0 ,1 ,⋯,0 ,1 ,⋯。
이제 계수 MM 이 주어지면M , 가장 작은n > 0 n > 0을N>0 이므로fib (n ) mod M = 0 , fib (n + 1 ) mod M = 1 \mathrm{fib}(n) \bmod M = 0 , \mathrm{fib}(n + 1) \bmod M = 1섬유 ( n )모드중=0 ,섬유 ( n+1 )모드중=1。
입력 형식
양의 정수 MM 이 있는 행을 입력하십시오.남。
출력 형식
양의 정수 nn 이 있는 라인 출력n。
예 #1
샘플 입력 #1
2
샘플 출력 #1
3
예 #2
샘플 입력 #2
6
샘플 출력 #2
24
힌트
예 1 설명
피보나치 수열은 0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , ⋯ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, \cdots0 ,1 ,1 ,2 ,3 ,5 ,8 ,13 ,21 ,34 ,⋯ , 쌍2 22 계수를 취한 결과는0 , 1 , 1 , 0 , 1 , 1 , 0 , 1 , 1 , 0 , ⋯ 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, \cdots0 ,1 ,1 ,0 ,1 ,1 ,0 ,1 ,1 ,0 ,⋯。
n = 3일 때 n = 3 임을 알 수 있습니다.N=세 번째 ,f(n) mod 2 = 0, f(n + 1) mod 2 = 1 f(n) \bmod 2= 0, f(n + 1) \bmod 2 =에프 ( 엔 )모드2=0 ,에프 ( 엔+1 )모드2=1 , 우리가 요구하는nnn 의 최소값입니다 .
데이터 범위
30 % 30\%데이터의 30% , M ≤ 18 M \leq 18중≤18 ;
70 % 70\%데이터의 70% , M ≤ 2018 M \leq 2018중≤2018 ;
100 % 100\%데이터의 100% , 2 ≤ M ≤ 706150 = 2 \leq M \leq 706150=2≤중≤706150=0xAC666
。
힌트
모듈로 ( mod )(\bmod)가 무엇인지 모른다면(모드) , 그러면 모듈로 연산은 수직 나눗셈의 마지막 "나눌 수 없는" 부분인 정수 나눗셈으로 얻은 나머지를 찾는 것입니다. 즉, a
mod M = k ⟺ a = b M + k ( M > 0 , 0 ≤ k < M ) a \bmod M =k \iff a = bM + k\ (M > 0, 0 \leq k < M)ㅏ모드중=케이⟺ㅏ=ㄴ 남+케이 ( 엠 >0 ,0≤케이<M )
其中a, b, ka, b, k, _b ,k는 모두 음수가 아닌 정수입니다.
C
/ 를 사용하면 모듈로 산술을 수행하는 데 C++
사용할 수 있습니다 %
.
를 사용하면 모듈로 산술을 수행하는 데 Pascal
사용할 수 있습니다 .mod
이 문제는 RE하기 매우 쉽습니다. 하나는 모듈러스를 취하는 것입니다. 그렇지 않으면 RE하기 쉽습니다. 그러면 m의 크기를 알려주지만 m*m인 경우 n의 최소 크기를 결정할 수 없습니다. , 그러면 배열이 폭발할 것입니다. 가장 좋은 방법은 롤링 배열을 사용하는 것입니다. 물론 공백 대신 재귀만 사용하도록 검색어를 외울 수도 있습니다.
내 잘못된 접근:
마지막 2개의 테스트 샘플이 RE였기 때문에 80점만 얻었고 n이 매우 클 수 있어야 합니다.
#include<bits/stdc++.h>
using namespace std;
const int N=706155;
int f[N];
int m;
void fib()
{
for(int i=2;i<N;i++)
f[i]=(f[i-1]+f[i-2])%m;
}
int main()
{
scanf("%d",&m);
f[0]=0,f[1]=1;
fib();
for(int i=1;i<m*m;i++)
{
if(f[i]==0&&f[i+1]==1)
{
printf("%d",i);return 0;}
}
return 0;
}
솔루션 1 - 메모이제이션 검색 긍정적인 솔루션:
#include<cstdio>
typedef long long ll;
using namespace std;
const ll INF=0x7fffffff;
ll fp[10000002];//记忆数组,虽然m不大但是不知道n多大,尽量开大,不过1千万差不多极限
ll m;
ll f(ll i)
{
if(fp[i])return fp[i];//调取记忆
if(i==1||i==2)return fp[i]=1%m;
else return fp[i]=(f(i-1)+f(i-2))%m;//这时就顺带%m可以使主程序更简单
}
int main()
{
scanf("%lld",&m);
ll i=1;//枚举
while(f(i)!=0||f(i+1)!=1)//题目要求
{
i++;
}
printf("%lld",i);
return 0;
}
솔루션 2 - 롤링 어레이
우리는 이 두 숫자가 매번 우리의 요구 사항을 충족하는지 여부만 고려하기 때문에 이 두 숫자는 다음 숫자와 관련이 있으므로 롤링 배열을 사용하여 이 세 숫자를 저장하는 것이 좋습니다.
#include<iostream>
#include<cstdio>
using namespace std;
int fi[5],m;
int main()
{
scanf("%d",&m);
fi[1]=0; fi[2]=1;
for(int i=1;;i++)
{
fi[3]=(fi[1]+fi[2])%m;
if(fi[2]==0&&fi[3]==1)
{
printf("%d\n",i);
return 0;
}
fi[1]=fi[2]; fi[2]=fi[3];
}
}