【HDU - 4794】Arnold【斐波那契数列循环节】

题意:

给出一个 N N N*N 的方格,每一次变换, ( x , y ) (x,y) 变成 ( ( x + y ) % N , ( x + 2 y ) % N ) ((x+y)\%N,(x+2*y)\%N) ,问经过最少多少次变换,可以变换成原来的方格。 ( 2 N 4 1 0 9 ) (2\leq N\leq 4*10^9)


思路:

先打表,对于每一个格子打表找到最少步数,然后整个方格的最少步数就是每个格子的 l c m lcm

然后打表可以发现,最后的答案取决与 ( n 1 , n 1 ) (n-1,n-1) 这个格子经过最少多少次变换之后可以变回到 ( n 1 , n 1 ) (n-1,n-1)

然后可以发现这是一个斐波那契数列的循环节, ( a , a ) ( 2 a , 3 a ) ( 5 a , 8 a ) ( 13 a , 21 a ) (a,a)-(2a,3a)-(5a,8a)-(13a,21a) ,即 ( f [ 1 ] , f [ 2 ] ) ( f [ 3 ] , f [ 4 ] ) ( f [ 5 ] , f [ 6 ] ) (f[1],f[2])-(f[3],f[4])-(f[5],f[6]) f [ 1 + m ] % n = f [ 1 ] % n f[1+m]\%n=f[1]\%n ,找到最小的 m m

因此直接上斐波那契循环节的板子。


代码:

#include <bits/stdc++.h>

#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
using namespace std;

#define ll unsigned long long

ll gcd(ll a, ll b) {
    return b == 0 ? a : gcd(b, a % b);
}

ll lcm(ll a, ll b) {
    return a / gcd(a, b) * b;
}

struct Matrix {
    ll mat[2][2];

    Matrix() { memset(mat, 0, sizeof mat); }
};

Matrix mul_M(Matrix a, Matrix b, ll mod) {
    Matrix ans;
    for (int i = 0; i < 2; ++i) {
        for (int j = 0; j < 2; ++j) {
            ans.mat[i][j] = 0;
            for (int k = 0; k < 2; ++k) {
                ans.mat[i][j] += a.mat[i][k] * b.mat[k][j] % mod;
                if (ans.mat[i][j] >= mod)ans.mat[i][j] -= mod;
            }
        }
    }
    return ans;
}

Matrix pow_M(Matrix a, ll n, ll mod) {
    Matrix ans;
    for (int i = 0; i < 2; ++i) ans.mat[i][i] = 1;
    Matrix tmp = a;
    while (n) {
        if (n & 1)ans = mul_M(ans, tmp, mod);
        tmp = mul_M(tmp, tmp, mod);
        n >>= 1;
    }
    return ans;
}

ll pow_m(ll a, ll n, ll mod) {
    ll ans = 1, tmp = a % mod;
    while (n) {
        if (n & 1)ans = ans * tmp % mod;
        tmp = tmp * tmp % mod;
        n >>= 1;
    }
    return ans;
}

const int maxn = 1e6 + 10;

int prime[maxn];

void getprime() {
    memset(prime, 0, sizeof prime);
    for (int i = 2; i <= maxn; ++i) {
        if (!prime[i])prime[++prime[0]] = i;
        for (int j = 1; j <= prime[0] && prime[j] * i <= maxn; ++j) {
            prime[prime[j] * i] = 1;
            if (i % prime[j] == 0)break;
        }
    }
}

ll factor[100][2];
int fatcnt;

int getfactors(ll x) {
    fatcnt = 0;
    ll tmp = x;
    for (int i = 1; prime[i] * prime[i] <= tmp; ++i) {
        factor[fatcnt][1] = 0;
        if (tmp % prime[i] == 0) {
            factor[fatcnt][0] = prime[i];
            while (tmp % prime[i] == 0) {
                factor[fatcnt][1]++;
                tmp /= prime[i];
            }
            fatcnt++;
        }
    }
    if (tmp != 1) {
        factor[fatcnt][0] = tmp;
        factor[fatcnt++][1] = 1;
    }
    return fatcnt;
}

int legendre(ll a, ll p) {
    if (pow_m(a, (p - 1) >> 1, p) == 1)return 1;
    else return -1;
}

int f0 = 1;
int f1 = 1;

ll getfib(ll n, ll mod) {
    if (mod == 1)return 0;
    Matrix A;
    A.mat[0][0] = 0;
    A.mat[1][0] = 1;
    A.mat[0][1] = 1;
    A.mat[1][1] = 1;
    Matrix B = pow_M(A, n, mod);
    ll ans = f0 * B.mat[0][0] + f1 * B.mat[1][0];
    return ans % mod;
}

ll fac[maxn];

ll G(ll p) {
    ll num;
    if (legendre(5, p) == 1)num = p - 1;
    else num = 2 * (p + 1);
    int cnt = 0;
    for (ll i = 1; i * i <= num; ++i) {
        if (num % i == 0) {
            fac[cnt++] = i;
            if (i * i != num) {
                fac[cnt++] = num / i;
            }
        }
    }
    sort(fac, fac + cnt);
    ll ans;
    for (int i = 0; i < cnt; ++i) {
        if (getfib(fac[i], p) == f0 && getfib(fac[i] + 1, p) == f1) {
            ans = fac[i];
            break;
        }
    }
    return ans;
}

ll find_loop(ll n) {
    getfactors(n);
    ll ans = 1;
    for (int i = 0; i < fatcnt; ++i) {
        ll record = 1;
        if (factor[i][0] == 2)record = 3;
        else if (factor[i][0] == 3)record = 8;
        else if (factor[i][0] == 5)record = 20;
        else record = G(factor[i][0]);
        for (int j = 1; j < factor[i][1]; ++j) {
            record *= factor[i][0];
        }
        ans = lcm(ans, record);
    }
    return ans;
}

int main() {
    __;
    getprime();
    ll n;
    while (cin >> n) {
        ll ans = find_loop(n);
        cout << ans / 2ll << endl;
    }
    return 0;
}
发布了244 篇原创文章 · 获赞 115 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_41552508/article/details/100077613