51nod1355-斐波那契的最小公倍數【min-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
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)=T⊆S∏gcd(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}}
這個好像算起來很麻煩,我們可以分開考慮每個
g
c
d
gcd
gcd的貢獻。
定義
f
n
=
∏
d
∣
n
g
d
f_n=\prod_{d|n}g_d
fn=∏d∣ngd
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)=T⊆S∏⎝⎛d∣gcd(T)∏gd
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)=∏gd∑T⊆S[d∣gcd(T)](−1)∣T∣+1
然後就是
∑
T
⊆
S
[
d
∣
g
c
d
(
T
)
]
(
−
1
)
∣
T
∣
+
1
\sum_{T\subseteq S}[d|gcd(T)](-1)^{|T|+1}
∑T⊆S[d∣gcd(T)](−1)∣T∣+1,因為沒有了空集,這個東西其實就相當於
[
∃
a
i
∈
S
,
d
∣
a
i
]
[\exists a_i\in S,d|a_i]
[∃ai∈S,d∣ai]。然後就可以直接列舉每個
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)=∃ai∈S,d∣ai∏gd
考慮 g g g怎麼構造,我們有 f n = ∏ d ∣ n g d f_n=\prod_{d|n}g_d fn=∏d∣ngd,直接移項就是 g n = f n − ∏ d ∣ n , d ≠ n g d g_n=f_n-\prod_{d|n,d\neq n}g_d gn=fn−∏d∣n,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;
}