[题解]斐波那契的最小公倍数

\[\color{red}{\textsf{小游者,真神人也,左马桶,右永神,会执利笔破邪炁,何人当之?}} \\ \begin{array}{|} \hline \color{pink}{\text{A small swimmer is a God.}} \\ \color{pink}{\text{The left toilet and the right eternal God}} \\ \color{pink}{\text{can break the evil energy with a sharp pen.}} \\ \color{pink}{\text{Who can resist him? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{green}{\text{小遊者は、神であり、左便器、右永神であり}} \\ \color{green}{\text{鋭いペンを持って真実を突き刺している。誰が彼に抵抗できるだろうか? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{lightblue}{\text{Petit voyageur, est Dieu aussi, toilettes gauche, Dieu éternel droit,}} \\ \color{lightblue}{\text{peut tenir un stylo tranchant pour briser le mal, qui devrait le faire?}} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{purple}{\text{Der Direktor ist wirklich ein Gott}} \\ \color{purple}{\text{mit einer Toilette links und Yongshen rechts}} \\ \color{purple}{\text{der einen spitzen Stift hält}} \\ \color{purple}{\text{um die Wahrheit zu durchdringen.}} \\ \color{purple}{\text{Wer kann ihm widerstehen? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{cyan}{\text{Ein kleiner Schwimmer ist ein Gott.}} \\ \color{cyan}{\text{Die linke Toilette und der rechte ewige Gott können }} \\ \color{cyan}{\text{die böse Energie mit einem scharfen Stift brechen.}} \\ \color{cyan}{\text{Wer sollte es sein?}} \\ \hline \end{array} \\ \color{red}{\textsf{对曰:“无人,狗欲当之,还请赐教!”}} \\ \newcommand\brak[1]{\left({#1}\right)} \newcommand\Brak[1]{\left\{{#1}\right\}} \newcommand\d[0]{\text{d}} \newcommand\string[2]{\genfrac{\{}{\}}{0pt}{}{#1}{#2}} \newcommand\down[2]{{#1}^{\underline{#2}}} \newcommand\ddiv[2]{\left\lfloor\frac{#1}{#2}\right\rfloor} \newcommand\udiv[2]{\left\lceil\frac{#1}{#2}\right\rceil} \newcommand\lcm[0]{\operatorname{lcm}} \newcommand\set[1]{\left\{{#1}\right\}} \newcommand\ceil[1]{\left\lceil{#1}\right\rceil} \newcommand\floor[1]{\left\lfloor{#1}\right\rfloor} \newcommand\rhs[1]{\;\text{Rhs}\;#1} \newcommand\lhs[1]{\;\text{Lhs}\;#1} \newcommand\Vec[1]{\vec{\mathbf{#1}}} \newcommand\rank[0]{\text{rank}} \newcommand\group[1]{\left\langle\right\rangle} \newcommand\norm[1]{\left|{#1}\right|} \]

题解

直接算每个质数的指数,对于每个质数 \(p_i\),设 \(F(p,a_i)\) 表示斐波那契数列的 \(F(a_i)\) 含有 \(p\) 多少。

\[\max\set{F(p,a_i)}=\sum_{T\subset U}(-1)^{\norm T+1}\min\set{F(p,a_j)|a_j\in T} \]

发现是 \(\lcm\)\(\gcd\) 的容斥:

\[\begin{aligned} \lcm{F(a_i)}&=\prod_{T\subset U}\gcd(F(a_i))^{(-1)^{\norm T+1}} \\ &=\prod_{T\subset U}F(\gcd(a_i))^{(-1)^{\norm T+1}} \end{aligned} \]

设在 \(\set {a_i}\) 中选择 \(k\) 个数,他们的 \(\gcd\)\(d\) 的方案数为 \(g(k,d)\),是 \(d\) 的倍数的方案数为 \(f(k,d)\),那么显然有

\[f(k,n)=\sum_{d\mid n}g(k,d)\Rightarrow g(k,n)=\sum_{n\mid d}\mu\brak{d\over n}f(k,d) \]

而我们要求的是

\[\lcm=\prod_{i=1}^\infty F(i)^{\sum_{j=1}^n (-1)^{j+1}\times g(j,i)} \]

专注于指数的运算:

\[\sum_{j=1}^n(-1)^{j+1}g(j,i)=\sum_{j=1}^n(-1)^{j+1}\sum_{i\mid d} \mu\brak{d\over i}f(j,d)=\sum_{i\mid d}\mu\brak{d\over i}\sum_{j=1}^n(-1)^{j+1}f(j,d) \]

\(\set {a_i}\)\(d\) 的倍数有 \(cnt(d)\) 个,那么 \(\displaystyle f(j,d)={cnt(d)\choose j}\),继续

\[(*)=\sum_{i\mid d}\mu\brak{d\over i}\sum_{j=1}^n(-1)^{j+1}{cnt(d)\choose j} \]

显然 \(cnt(d)\le n\),因此可以将后面的东西改一下

\[(*)=\sum_{i\mid d}\mu\brak{d\over i}\sum_{j=1}^{cnt(d)}(-1)^{j+1}{cnt(d)\choose j} \]

注意到 \(\displaystyle \sum_{j=0}^{cnt(d)}(-1)^{j+1}{cnt(d)\choose d}=-1\times \sum_{j=0}^{cnt(d)}(-1)^j{cnt(d)\choose j}=-(1-1)^{cnt(d)}=0\),因此 \(\displaystyle \sum_{j=1}^{cnt(j)}(-1)^{j+1}{cnt(d)\choose j}=[cnt(j)>0]\),所以

\[(*)=\sum_{i\mid d}\mu\brak{d\over i}[cnt(d)>0] \]

对于每一个 \(F(i)\),都计算一下它头顶上的系数,直接暴力枚举 \(d\) 就行了,因为这是 \(\mathcal O(A\ln A)\) 的,其中 \(A=\max a_i\).

参考代码

/** @author __Elaina__ */

// #pragma GCC optimize("Ofast")
// #pragma GCC optimize("inline")
// #define GCC optimize(2)
// #define GCC optimize(3)

#include <bits/stdc++.h>
using namespace std;

// #define USING_FREAD
// #define NDEBUG
#include <cassert>

namespace Elaina {
/** その可憐な少女は魔女であり、旅人でした。 ―― そう、私です! */

#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define repf(i, l, r) for (int i = (l), i##_end_ = (r); i < i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
/** @warning no forced type conversion */
#define rqr(x) ((x) * (x))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
#define masdf(...) fprintf(stderr, __VA_ARGS__)

typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int, int> pii;
typedef vector<int> vset;

template<class T> inline T fab(T x) { return x < 0 ? -x : x; }
template<class T> inline void chkmin(T& x, const T rhs) { x = std::min(x, rhs); }
template<class T> inline void chkmax(T& x, const T rhs) { x = std::max(x, rhs); }

#ifdef USING_FREAD
inline char qkgetc() {
# define BUFFERSIZE 1 << 20
    static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
    return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
}
# define CHARRECEI qkgetc()
#else
# define CHARRECEI ((char)getchar())
#endif

template<class T> inline T readret(T x) {
    x = 0; int f = 0; char c;
    while (!isdigit(c = CHARRECEI)) if(c == '-') f = 1;
    for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
    return f ? -x : x;
}
template<class T> inline void readin(T& x) {
    x = 0; int f = 0; char c;
    while (!isdigit(c = CHARRECEI)) if (c == '-') f = 1;
    for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
    if (f) x = -x;
}
template<class T, class... Args> inline void readin(T& x, Args&... args) {
    readin(x), readin(args...);
}
template<class T> inline void writln(T x, char c = '\n') {
    if (x < 0) putchar('-'), x = -x;
    static int __stk[55], __bit = 0;
    do __stk[++__bit] = x % 10, x /= 10; while (x);
    while (__bit) putchar(__stk[__bit--] ^ 48);
    putchar(c);
}
template<class T> inline T listMax(const T& x) { return x; }
template<class T, class... Args> inline T listMax(const T& x, const Args&... args) {
    return max(x, listMax(args...));
}
template<class T> inline T listMin(const T& x) { return x; }
template<class T, class... Args> inline T listMin(const T& x, const Args&... args) {
    return min(x, listMin(args...));
}

} // namespace Elaina
using namespace Elaina;

const int Maxn = 1e6;
const int mod = 1e9 + 7;

inline int qkpow(int a, int n) {
    int ret = 1;
    for (; n > 0; n >>= 1, a = (int)(1ll * a * a % mod))
        if (n & 1) ret = (int)(1ll * ret * a % mod);
    return ret;
}

int a[Maxn + 5], n;
int cnt[Maxn + 5], buc[Maxn + 5];

bool vis[Maxn + 5];
int prime[Maxn + 5], mu[Maxn + 5];
inline void sieve() {
    vis[1] = true, mu[1] = 1;
    rep (i, 2, Maxn) {
        if (!vis[i]) prime[++*prime] = i, mu[i] = -1;
        for (int j = 1; j <= *prime && i * prime[j] <= Maxn; ++j) {
            vis[i * prime[j]] = true;
            if (i % prime[j] == 0) { mu[i * prime[j]] = 0; break; }
            mu[i * prime[j]] = -mu[i];
        }
    }
}

signed main() {
    readin(n);
    rep (i, 1, n) ++buc[readret(1)];
    rep (i, 1, Maxn)
        for (int j = i; j <= Maxn; j += i)
            cnt[i] += buc[j];
    sieve();
    int fl = 0, fc = 1, ans = 1, tmp;
    rep (i, 1, Maxn) {
        int p = 0;
        for (int d = i; d <= Maxn; d += i) if (cnt[d] > 0) p += mu[d / i];
        if (p < 0) tmp = qkpow(fc, mod - 2), p = -p;
        else tmp = fc;
        ans = (int)(1ll * ans * qkpow(tmp, p) % mod);
        tmp = (fl + fc) % mod, fl = fc, fc = tmp;
    }
    writln(ans);
    return 0;
}
posted @ 2022-03-30 21:04  Arextre  阅读(122)  评论(0编辑  收藏  举报