ZOJ-3707 斐波那契 数论

该博客主要探讨ZOJ-3707题目,研究如何找到满足特定条件的Prime S,即集合中不包含连续数字且其元素的最大公约数为1的子集。博主通过动态规划计算出s[n]的值,并发现它与斐波那契数列的关系。接着,他们提出一个基于质数的策略来确定Prime S,并使用同余方程求解最小满足条件的s[n]。最后,给出了问题的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

vjudge链接

题意

定义 s [ n ] s[n] s[n]为集合 { 1 , 2 , 3 , ⋯   , n } \{1, 2, 3, \cdots, n\} {1,2,3,,n}中不包含连续数字的子集个数。如果 s [ n ] s[n] s[n]满足对于任意的 i ϵ [ 1 , n ) i\epsilon[1, n) iϵ[1,n)都有 g c d ( s [ i ] , s [ n ] ) = 1 gcd(s[i], s[n]) = 1 gcd(s[i],s[n])=1则称 s [ n ] s[n] s[n]是Prime S,其中 s [ 1 ] s[1] s[1]是Prime S。现问不小于第k小的Prime S且能被x整除的 s [ n ] s[n] s[n]最小是多少,输出 s [ n ] x m o d    m \frac{s[n]}{x}\mod m xs[n]modm

题解

首先可以dp出s[n]的值,方程为 d p [ i ] [ 0 ] = d p [ i − 1 ] [ 0 ] + d p [ i − 1 ] [ 1 ] dp[i][0] = dp[i-1][0]+dp[i-1][1] dp[i][0]=dp[i1][0]+dp[i1][1] d p [ i ] [ 1 ] = d p [ i − 1 ] [ 0 ] dp[i][1] = dp[i-1][0] dp[i][1]=dp[i1][0]
其中 s [ i ] = d p [ i ] [ 0 ] + d p [ i ] [ 1 ] s[i] = dp[i][0]+dp[i][1] s[i]=dp[i][0]+dp[i][1]
然后列举s[i]发现是第3项开始的斐波那契数列即 s [ n ] = f i b [ n + 2 ] s[n] = fib[n+2] s[n]=fib[n+2]
然后按照题意暴力得到Prime S,打表发现如果s[n]是Prime S则一定有n+2为质数或者4。
因此我们可以列出一个“质数”数列 p = 3 , 4 , 5 , 7 , 11 , 13 , ⋯ p = 3,4,5,7,11,13,\cdots p=3,4,5,7,11,13,,则第k个Prime S为p[k],因此从p[k]项开始枚举斐波那契数列判断是否被x整除即可。
然后利用同余方程 a b m o d    m = a m o d    m × x x \frac{a}{b}\mod m = \frac{a\mod m\times x}{x} bamodm=xamodm×x输出答案。

代码
#include <bits/stdc++.h>
const int N = 17000005;
long long mod;
struct matrix {
    int n, m;
    std::vector<std::vector<long long> > mat;
    matrix(int _n, int _m) : n(_n), m(_m) {
        std::vector<long long> tmp(m);
        for (int i = 0; i < n; ++i) mat.push_back(tmp);
    }
    matrix& operator*=(const matrix& t) {
        matrix res(n, t.m);
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < t.m; ++j) {
                for (int k = 0; k < m; ++k) {
                    res.mat[i][j] += (mat[i][k] * t.mat[k][j]);
                    res.mat[i][j] %= mod;
                }
            }
        }
        *this = res;
        return *this;
    }
} a = matrix(2, 2), res = matrix(2, 2);

std::vector<long long> prime;
bool isprime[N];
void init(int MAX = N) {
    isprime[1] = true;
    memset(isprime, false, sizeof(isprime));
    for (int i = 2; i < MAX; ++i) {
        if (!isprime[i]) prime.push_back(i);
        for (int j = 0; j < (int)prime.size() && i * prime[j] < MAX; ++j) {
            isprime[i * prime[j]] = true;
        }
    }
    prime.insert(prime.begin() + 2, 4);
}

long long ksm(long long k) {
    res.mat[0][0] = res.mat[1][1] = 1;
    res.mat[0][1] = res.mat[1][0] = 0;
    a.mat[0][0] = a.mat[0][1] = a.mat[1][0] = 1;
    a.mat[1][1] = 0;
    while (k) {
        if (k & 1) res *= a;
        a *= a;
        k >>= 1;
    }
    return (res.mat[0][0] + res.mat[0][1]) % mod;
}

int main() {
    init();
    int t;
    int k, x, m;
    long long pos;
    scanf("%d", &t);
    while (t--) {
        scanf("%d%d%d", &k, &x, &m);
        pos = prime[k];
        mod = x;
        while (ksm(pos - 2) != 0) pos++;
        mod = m * x;
        printf("%lld\n", ksm(pos - 2) / x);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值