题意:
给出一个正整数 n ( n ≤ 1 0 10000 ) n(n\leq10^{10000}) n(n≤1010000),定义整数拆分的权值为以拆分的所有数为下标的斐波那契数的乘积,形式化的说,假若将 n n n分解为一个长 m m m的有序序列 [ c 1 , c 2 , c 3 , . . , c m ] [c_{1},c_{2},c_{3},..,c_{m}] [c1,c2,c3,..,cm],并且 f i f_{i} fi为斐波那契数列的第 i i i项,那么这一次拆分的权值为 f c 1 f c 2 . . . f c m f_{c_{1}}f_{c_{2}}...f_{c_{m}} fc1fc2...fcm,求拆分 n n n的所有权值总和
Solution:
假若我们把 n n n分成 k k k个数,那么 k ∈ [ 1 , n ] k\in[1,n] k∈[1,n],并且每个数的生成函数为
f ( x ) = f 0 + f 1 x + f 2 x 2 + . . . . f(x)=f_{0}+f_{1}x+f_{2}x^2+.... f(x)=f0+f1x+f2x2+....
那么此次拆分的总权值的生成函数为
f k ( x ) f^k(x) fk(x)
枚举所有的 k k k,答案即
F ( x ) = ∑ k = 1 ∞ f k ( x ) F(x)=\sum_{k=1}^{\infty}f^k(x) F(x)=k=1∑∞fk(x)
其中 f ( x ) f(x) f(x)就是斐波那契数列的生成函数,有
f ( x ) = x 1 − x − x 2 f(x)=\frac{x}{1-x-x^2} f(x)=1−x−x2x
由于
1 1 − x = ∑ i = 0 ∞ x i \frac{1}{1-x}=\sum_{i=0}^{\infty}x^i 1−x1=i=0∑∞xi
所以
F ( x ) = 1 1 − f ( x ) = 1 − x − x 2 1 − 2 x − x 2 F(x)=\frac{1}{1-f(x)}=\frac{1-x-x^2}{1-2x-x^2} F(x)=1−f(x)1=1−2x−x21−x−x2
接下来只需要在 x = 0 x=0 x=0处展开 F ( x ) F(x) F(x),由于分子比较复杂,尝试分离常数,得到
F ( x ) = 1 + x 1 − 2 x − x 2 F(x)=1+\frac{x}{1-2x-x^2} F(x)=1+1−2x−x2x
接下来只需要对 1 1 − 2 x − x 2 \frac{1}{1-2x-x^2} 1−2x−x21分解因式,直接给出分解结果
x 1 = − 1 + 2 , x 2 = − 1 − 2 , g ( x ) = 1 − 1 x 1 − x 2 ( 1 x − x 1 − 1 x − x 2 ) x_{1}=-1+\sqrt{2},x_{2}=-1-\sqrt{2},g(x)=1-\frac{1}{x_{1}-x_{2}}(\frac{1}{x-x_{1}}-\frac{1}{x-x_{2}}) x1=−1+2,x2=−1−2,g(x)=1−x1−x21(x−x11−x−x21)
这里需要注意的是,我们都是把分母分解为 ( x − x 1 ) ( x − x 2 ) (x-x_{1})(x-x_{2}) (x−x1)(x−x2),此时 x 2 x^{2} x2变为正号,如果分解前 x 2 x^2 x2有负号,需要注意符号转换
再分解 1 x + k \frac{1}{x+k} x+k1的形式,最后结果为
f ( x ) = 1 − x x 1 − x 2 ( − 1 x 1 ∑ i = 0 ∞ ( x x 1 ) i + 1 x 2 ∑ i = 0 ∞ ( x x 2 ) i ) f(x)=1-\frac{x}{x_{1}-x_{2}}(-\frac{1}{x_{1}}\sum_{i=0}^{\infty}(\frac{x}{x_{1}})^i+\frac{1}{x_{2}}\sum_{i=0}^{\infty}(\frac{x}{x_{2}})^i) f(x)=1−x1−x2x(−x11i=0∑∞(x1x)i+x21i=0∑∞(x2x)i)
代入根即可,需要注意的是,当我们求 x k ( k > 0 ) x^k(k>0) xk(k>0)项系数时,最前面那个1是不考虑的,因为他被加到 x 0 x^0 x0项了,所以最后 x n x^n xn的系数为
2 4 ( ( 1 + 2 ) n − ( 1 − 2 ) n ) \frac{\sqrt{2}}{4}((1+\sqrt{2})^n-(1-\sqrt{2})^n) 42((1+2)n−(1−2)n)
其中,由于幂过大,由费马小定理
a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv 1\ (mod\ p) ap−1≡1 (mod p)
可以得到
a n ≡ a n m o d ( p − 1 ) ( m o d p ) a^n\equiv a^{n\ mod(p-1)}\ (mod\ p) an≡an mod(p−1) (mod p)
于是对幂模上 p − 1 p-1 p−1,然后快速幂即可
// #include<bits/stdc++.h>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<cstdio>
#include<time.h>
using namespace std;
using ll=long long;
const int N=205,inf=0x3fffffff;
const long long INF=0x3f3f3f3f3f3f,mod=1e9+7;
ll qpow(ll a,ll b)
{
ll ret=1,base=a;
while(b)
{
if(b&1) ret=ret*base%mod;
base=base*base%mod;
b>>=1;
}
return ret;
}
ll inv(ll x){return qpow(x,mod-2);}
const long long inv2=inv(2),inv3=inv(3);
namespace poly
{
//多项式开根二次剩余部分
ll si;
struct Complex_MOD
{
ll a,b;
Complex_MOD operator*(const Complex_MOD &t) const
{
Complex_MOD res;
res.a=(a*t.a%mod+b*t.b%mod*si%mod)%mod;
res.b=(a*t.b%mod+b*t.a%mod)%mod;
return res;
}
};
inline ll Pow_Complex(Complex_MOD val,ll b)
{
Complex_MOD res={1,0};
while(b)
{
if(b&1) res=res*val;
val=val*val;
b>>=1;
}
return res.a;
}
ll squaremod(ll n)
{
if(!n) return 0;
srand((unsigned)(time(NULL)));
ll p1,p2;
while(1)
{
p1=1ll*rand()*rand()%mod;
p2=(p1*p1%mod-n+mod)%mod;
if(::qpow(p2,(mod-1)/2)!=1) break;
}
si=p2;
Complex_MOD val={p1,1};
int ans=Pow_Complex(val,(mod+1)/2);
if(mod-ans<ans) ans=mod-ans;
return ans;
}
//以上是二次剩余
int pos[N],invs[N],invcnt,sqrts[N],sqrtcnt,exps[N],expcnt;
ll a[N],b[N],invtmp[N],dertmp[N],multitmp[N],lntmp[N],lnlntmp[N],qpowtmp[N];
int getlen(int k)
{
int ret=0;
while(k){ret++;k>>=1;}
return ret;
}
int getrev(int k,int len)
{
int ret=0;
while(k){ret=(ret<<1|(k&1));k>>=1;len--;}
return ret<<len;
}
int getpos(int n)
{
int limit=1;
while(limit<=n) limit<<=1;
int len=getlen(limit-1);
for(int i=0;i<limit;i++) pos[i]=getrev(i,len);
return limit;
}
void ntt(ll *a,int limit,int op)
{
for(int i=0;i<limit;i++)
if(i<pos[i]) swap(a[i],a[pos[i]]);
for(int len=2;len<=limit;len<<=1)
{
ll base=::qpow(op==1?3:inv3,(mod-1)/len);
for(int l=0;l<limit;l+=len)
{
ll now=1;
for(int i=l;i<l+len/2;i++)
{
ll x=a[i]%mod,y=now*a[i+len/2]%mod;
a[i]=(x+y)%mod;
a[i+len/2]=(x-y+mod)%mod;
now=now*base%mod;
}
}
}
}
void prepare(int *s,int &cnt,int n)
{
cnt=0;
while(n>1) s[++cnt]=n,n=n+1>>1;
}
void inv(ll *f,ll *g,int n)//求出f的乘法逆元,放到g内,f的长度为n,需要保证g是空的
{
prepare(invs,invcnt,n);
g[0]=::inv(f[0]);
for(int i=invcnt;i>=1;i--)
{
int limit=getpos(invs[i]<<1);
memcpy(a,f,sizeof(ll)*invs[i]); fill(a+invs[i],a+limit,0);
ntt(a,limit,1); ntt(g,limit,1);
for(int i=0;i<limit;i++) g[i]=g[i]*((2-a[i]*g[i]%mod)%mod+mod)%mod;
ntt(g,limit,-1); ll tmp=::inv(limit);
for(int i=0;i<limit;i++) g[i]=g[i]*tmp%mod;
fill(g+invs[i],g+limit,0);
}
memset(a,0,sizeof(a));
}
void sqrt(ll *f,ll *g,int n)//多项式f开根,存放在g内,需要保证g是空的
{
prepare(sqrts,sqrtcnt,n); g[0]=squaremod(f[0]);//二次剩余
for(int i=sqrtcnt;i>=1;i--)
{
int limit=getpos(sqrts[i]<<1);
memset(invtmp,0,sizeof(ll)*limit); inv(g,invtmp,sqrts[i]);
memcpy(a,f,sizeof(ll)*sqrts[i]); fill(a+sqrts[i],a+limit,0);
ntt(g,limit,1); ntt(invtmp,limit,1); ntt(a,limit,1);
for(int i=0;i<limit;i++) g[i]=inv2*(g[i]+invtmp[i]*a[i]%mod)%mod;
ntt(g,limit,-1); ll tmp=::inv(limit);
for(int i=0;i<limit;i++) g[i]=g[i]*tmp%mod;
fill(g+sqrts[i],g+limit,0);
}
}
void derivation(ll *f,ll *g,int n)//f求导,放入g
{
for(int i=0;i<n-1;i++) g[i]=f[i+1]*(i+1);
g[n-1]=0;
}
void integral(ll *f,ll *g,int n)//f积分,放入g
{
g[0]=0;
for(int i=1;i<n;i++) g[i]=f[i-1]*::inv(i)%mod;
}
void multi(ll *f,ll *g,ll *t,int n,int m)//f*g放入t
{
int limit=getpos(n+m);
memcpy(a,f,sizeof(ll)*n); fill(a+n,a+limit,0);
memcpy(t,g,sizeof(ll)*m); fill(t+m,t+limit,0);
ntt(a,limit,1); ntt(t,limit,1);
for(int i=0;i<limit;i++) t[i]=t[i]*a[i]%mod;
ntt(t,limit,-1); ll tmp=::inv(limit);
for(int i=0;i<limit;i++) t[i]=t[i]*tmp%mod;
fill(t+n+m,t+limit,0);
}
void ln(ll *f,ll *g,int n)
{
derivation(f,dertmp,n);
int limit=1;
while(limit<=(n<<1)) limit<<=1;
memset(invtmp,0,sizeof(ll)*limit);
inv(f,invtmp,n);
multi(dertmp,invtmp,multitmp,n,n);
integral(multitmp,g,n);
}
void exp(ll *f,ll *g,int n)//e^(f)放入g,需要保证g是空的
{
prepare(exps,expcnt,n); g[0]=1;
for(int i=expcnt;i>=1;i--)
{
int limit=getpos(exps[i]<<1);
ln(g,lntmp,exps[i]); fill(lntmp+exps[i],lntmp+limit,0);
memcpy(a,f,sizeof(ll)*exps[i]); fill(a+exps[i],a+limit,0);
ntt(g,limit,1); ntt(lntmp,limit,1); ntt(a,limit,1);
for(int i=0;i<limit;i++) g[i]=g[i]*(((1-lntmp[i]+a[i])%mod+mod)%mod)%mod;
ntt(g,limit,-1); ll tmp=::inv(limit);
for(int i=0;i<limit;i++) g[i]=g[i]*tmp%mod;
fill(g+exps[i],g+limit,0);
memset(lntmp,0,sizeof(ll)*limit);
}
}
void read(char *s,int n,ll &k1,ll &k2,ll &k3)
{
k1=k2=k3=0; int len=strlen(s+1);
for(int i=1;i<=len;i++)
{
k1=(10*k1+(s[i]-'0'))%mod;
k2=(10*k2+(s[i]-'0'))%(mod-1);
if(k3<n) k3=10*k3+(s[i]-'0');
}
}
void qpow(ll *f,ll *g,char *s,int n)//字符串形式给出幂,f^(k)放入g中,需要保证g是空的
{
ll k1,k2,k3; int sta=0;
read(s,n,k1,k2,k3);
while(sta<n&&f[sta]==0) sta++;
if(sta==n||sta*k1>n||(f[0]==0&&k3>=n))
{
for(int i=0;i<n;i++) g[i]=0;
return;
}
ll invsta=::inv(f[sta]),tmp=::qpow(f[sta],k2);
for(int i=0;i<n;i++) qpowtmp[i]=f[i];
for(int i=0;i<n;i++) qpowtmp[i]=qpowtmp[i+sta]*invsta%mod;
ln(qpowtmp,lnlntmp,n);
for(int i=0;i<n;i++) lnlntmp[i]=lnlntmp[i]*k1%mod;
exp(lnlntmp,g,n);
for(int i=n-1;i>=sta*k1;i--) g[i]=g[i-sta*k1]*tmp%mod;
for(int i=0;i<sta*k1;i++) g[i]=0;
}
};
ll n;
const long long invsqrt2=poly::squaremod(2),inv4=::inv(4);
inline ll read()
{
ll ret=0;
char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch))
{
ret=(ret*10%(mod-1)+ch-'0')%(mod-1);
ch=getchar();
}
return ret;
}
int main()
{
n=read();
cout<<invsqrt2*inv(4)%mod*(((qpow(1+invsqrt2,n)-qpow(((1-invsqrt2)%mod+mod)%mod,n))%mod+mod)%mod)%mod;
return 0;
}