4833: [Lydsy2017年4月月賽]最小公倍佩爾數 數論變換

先吐槽一下:唐老師下標全崩了,最重要的後面的一坨推導全都掛了qwq,我推到倒數第二步然後和題解一對發現我好想全推錯了233333? 最後還是找的A了的zyz才弄明白他寫的是什麼

這題和一道斐波那契公倍數比較像,先說這個吧

先考慮給你的那個式子,你上面減下面再移個項就變成通項公式了

然後通過特徵根/觀察/打表你get了這個遞推式是f(i)=2f(i1)+f(i2)

觀察這個遞推式的性質,首先我們能夠得到gcd(f(i),f(i1))=1 ,原因是f(i)%f(i1)=f(i2) 然後無限遞歸一下就知道他們等於gcd(f(1),f(2))=1

然後考慮f(n+m)=f(n1)f(m)+f(n)f(m+1) ,我證這個的時候非常傻,用通項弄得,實際上考慮矩陣乘法的遞推式就可以了,注意到結合律之後拆出中間一個就好了

然後我們get了一個非常關鍵的結論是gcd(f(i),f(j))=f(gcd(i,j)))

證明的話考慮一種讓i>j 那麼考慮令j=m ,i=n+m

此時gcd(f(n+m),f(m))=gcd(f(n1)f(m)+f(n)f(m+1),f(m))

前面那項顯然可以直接去掉了,因爲是f(m) 的倍數

那麼剩下的是gcd(f(n)f(m+1),f(m))

注意到gcd(f(m+1),f(m))=1 所以乘不乘他都一樣了

所以他等於gcd(f(n),f(m)) 遞歸即可

現在可以考慮g(i)=lcm(f(1),f(2),...,f(i))

考慮g(S)=lcm(f(S)) ,其中S是一個集合從1到n

然後我們子集反演一下就得到了g(S)=TSgcd(f(T))(1)|T|+1

注意到gcd(f(T))=f(gcd(T))

一個經典思路考慮構造數列h ,f(n)=d|nh(d)

把這個帶進去就有

g(S)=TSd|gcd(T)h(d)(1)|T|+1

考慮直接枚舉一個h(d) 的貢獻

g(S)=nd=1h(d)TS(1)iT(1)([d|i])

g(S)=nd=1h(d)1iS(1[d|i])

這個是怎麼來的呢?考慮這樣一個事情,對於一個元素來說他被選擇而且是d 的倍數的話他的貢獻是1 否則的話貢獻就是1

所以一個元素總的貢獻就是[1[d|i]] ,我們枚舉了所有子集,由於乘法原理每個數字之間一點關係都沒有所以把所有的貢獻乘在一起就好啦

但是注意到前面是符號所以是減去這個貢獻還有空集,空集的貢獻就是1,所以就有了上面的那個式子了

然後考慮後面那個東西在n=1 的時候等於0,此時g(1)=1 ,當n!=1 的時候總存在一個d 不能作爲所有i 的約數那麼就等於0,此時g(S)=nd=1h(d)

那麼h(d) 等於啥呢?

通過廣義莫比烏斯反演我們可以知道他等於同等運算在mu意義下的逆運算

f(n)=d|nh(d),h(n)=d|nf(d)μnd

問題完美解決,時間複雜度nlog(n)

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e6+5;
int prime[N], cnt, mu[N];
bool F[N];
typedef long long LL;

inline int pow(int a,int b,int mod) {
    LL res = 1;
    for(;b;b>>=1,a=(LL)a*a%mod)
        if(b&1)
            res = res * a % mod;
    return res;
}

inline void init() {
    const int Max = 1e6;
    mu[1] = 1;
    for(int i=2;i<=Max;++i) {
        if(!F[i]) mu[i] = -1, prime[++cnt] = i;
        for(int j=1;prime[j]*i<=Max;++j) {
            F[i*prime[j]] = 1;
            if(i%prime[j]==0) break;
            mu[i*prime[j]] = -mu[i];
        } 
    }
}

int f[N], h[N], g[N], inv[N];

int main() {
    int T;
    cin >> T;
    init();
    while(T--)  {
        int Max, mod;
        cin >> Max >> mod;
        f[1] = 1, f[2] = 2;
        register int i; int x;
        for(i=3;i<=Max;++i) f[i] = (2ll * f[i-1] + f[i-2]) % mod;
        for(i=1;i<=Max;++i) h[i] = 1;
        for(i=1;i<=Max;++i) inv[i] = pow(f[i], mod-2,mod);
        for(int d=1;d<=Max;++d) {
            for(i=1;i*d<=Max;++i) {
                x = i * d;
                if(mu[i] == 1) 
                    h[x] = (LL) h[x] * f[d] % mod;
                else if(mu[i] == -1) 
                    h[x] = (LL) h[x] * inv[d] % mod;
            }
        }
        for(i=2;i<=Max;++i) h[i] = (LL) h[i] * h[i-1] % mod;
        for(i=1;i<=Max;++i) g[i] = h[i];
        LL ans = 0;
        for(i=1;i<=Max;++i) ans = (ans + (LL) g[i] * i % mod) % mod;
        cout << ans << endl;
    }
}   
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章