1. 程式人生 > 其它 >51nod1355-斐波那契的最小公倍數【min-max容斥】

51nod1355-斐波那契的最小公倍數【min-max容斥】

技術標籤:數論and數學51nodmin-max容斥

正題

題目連結:http://www.51nod.com/Challenge/Problem.html#problemId=1355


題目大意

定義 f i f_i fi表示斐波那契的第 i i i項,給出一個大小為 n n n的集合 S S S l c m ( f S ) lcm(f_S) lcm(fS)


解題思路

如果每個質數的次數分開考慮,那麼 g c d gcd gcd就是次數取 m i n min min l c m lcm lcm就是次數取 m a x max max,所以可以套用 m i n − m a x min-max

minmax容斥的式子
l c m ( S ) = ∏ T ⊆ S g c d ( T ) ( − 1 ) ∣ T ∣ + 1 lcm(S)=\prod_{T\subseteq S}gcd(T)^{(-1)^{|T|+1}} lcm(S)=TSgcd(T)(1)T+1
然後因為 g c d ( f x , f y ) = f g c d ( x , y ) gcd(f_x,f_y)=f_{gcd(x,y)} gcd(fx,fy)=fgcd(x,y),那麼這題的答案
l c m ( f S ) = ∏ T ⊆ S f g c d ( T ) ( − 1 ) ∣ T ∣ + 1 lcm(f_S)=\prod_{T\subseteq S}f_{gcd(T)}^{(-1)^{|T|+1}}
lcm(fS)=TSfgcd(T)(1)T+1

這個好像算起來很麻煩,我們可以分開考慮每個 g c d gcd gcd的貢獻。
定義 f n = ∏ d ∣ n g d f_n=\prod_{d|n}g_d fn=dngd
l c m ( f S ) = ∏ T ⊆ S ( ∏ d ∣ g c d ( T ) g d ) ( − 1 ) ∣ T ∣ + 1 lcm(f_S)=\prod_{T\subseteq S}\left(\prod_{d|gcd(T)}g_d\right)^{(-1)^{|T|}+1} lcm(fS)=TSdgcd(T)gd
(1)T+1

l c m ( f S ) = ∏ g d ∑ T ⊆ S [ d ∣ g c d ( T ) ] ( − 1 ) ∣ T ∣ + 1 lcm(f_S)=\prod g_d^{\sum_{T\subseteq S}[d|gcd(T)](-1)^{|T|+1}} lcm(fS)=gdTS[dgcd(T)](1)T+1
然後就是 ∑ T ⊆ S [ d ∣ g c d ( T ) ] ( − 1 ) ∣ T ∣ + 1 \sum_{T\subseteq S}[d|gcd(T)](-1)^{|T|+1} TS[dgcd(T)](1)T+1,因為沒有了空集,這個東西其實就相當於 [ ∃ a i ∈ S , d ∣ a i ] [\exists a_i\in S,d|a_i] [aiS,dai]。然後就可以直接列舉每個 d d d來求答案了。
l c m ( f S ) = ∏ ∃ a i ∈ S , d ∣ a i g d lcm(f_S)=\prod_{\exists a_i\in S,d|a_i} g_d lcm(fS)=aiS,daigd

考慮 g g g怎麼構造,我們有 f n = ∏ d ∣ n g d f_n=\prod_{d|n}g_d fn=dngd,直接移項就是 g n = f n − ∏ d ∣ n , d ≠ n g d g_n=f_n-\prod_{d|n,d\neq n}g_d gn=fndn,d=ngd就好了。

時間複雜度 O ( n log ⁡ n ) O(n\log n) O(nlogn)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1e6+10,P=1e9+7;
ll n,m,g[N],ans;
bool v[N];
ll power(ll x,ll b){
    ll ans=1;
    while(b){
        if(b&1)ans=ans*x%P;
        x=x*x%P;b>>=1;
    }
    return ans;
}
signed main()
{
    scanf("%lld",&n);g[1]=ans=1;
    for(ll i=1;i<=n;i++){
        ll x;scanf("%lld",&x);
        m=max(m,x);v[x]=1;
    }
    for(ll i=2;i<=m;i++)g[i]=(g[i-1]+g[i-2])%P;
    for(ll i=1;i<=m;i++){
        ll inv=power(g[i],P-2);
        for(ll j=2*i;j<=m;j+=i)
            g[j]=g[j]*inv%P;
    }
    for(ll i=1;i<=m;i++){
        bool flag=0;
        for(ll j=i;j<=m;j+=i)
            if(v[j]){flag=1;break;}
        if(flag)ans=(ans*g[i])%P;
    }
    printf("%lld\n",ans);
    return 0;
}