끝의 시작(스크롤 배열, 메모화된 검색)

어떤 문

종말의 시작

주제 배경

드디어 출발점이 끝났고,
드디어 기간이 쓰여지고,
드디어 작별을 고
하고, 드디어 시작했던 곳으로 돌아왔습니다
...

각 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^22 값이므로M 2 M^22회 계산 후 주기가 있어야 합니다

더 일반적으로, 계수 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 1818 ;

70 % 70\%데이터의 70% , M ≤ 2018 M \leq 20182018 ;

100 % 100\%데이터의 100% , 2 ≤ M ≤ 706150 = 2 \leq M \leq 706150=2706150=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];
    }
}

추천

출처blog.csdn.net/qq_51408826/article/details/127561647