FZU 2314

FZU 第十六届程序设计竞赛_重现赛 & FOJ Problem 2314 宝宝会求导

已知 \(\displaystyle f(x)={1\over e^{-x}+1}\)

\(x=0\) 处泰勒展开第 \(k\) 项系数 \(\mod (10^9+7)\)


法一

已知该函数在 \(x=0\) 处泰勒展开式第 \(k\) 项系数为 \(\displaystyle {f^{(k)}(0)\over k!}\)

由于 \(({1\over k!})\mod (10^9+7)\) 可以 \(O(n)\) 求出,故直接考虑求解 \(f^{(k)}(0)\mod (10^9+7)\)

不妨设 \(\displaystyle y=f(x)={1\over e^{-x}+1}\Rightarrow {\text dy\over \text dx}=-{{\text d\over \text dx}(e^{-x}+1)\over (e^{-x}+1)^2}={(e^{-x}+1)-1\over (e^{-x}+1)^2}=y-y^2\)

再次求导可得 \(\displaystyle {\text d^2 y\over \text dx^2}={\text d\over \text dx}({\text dy\over \text dx})={\text dy\over \text dx}\cdot {\text d\over \text dy}({\text dy\over \text dx})={\text dy\over \text dx}\cdot {\text d\over \text dy}(y-y^2)=(1-2y)\cdot {\text dy\over \text dx}=(1-2y)(y-y^2)=y-3y^2+2y^3\)

进而可观察出规律,若设 \(\displaystyle {\text d^n y\over \text dx^n}=\sum_{i=1}^{n+1}a_iy^i\)

\(\displaystyle {\text d^{n+1} y\over \text dx^{n+1}}={\text d\over \text dx}({\text d^n y\over \text dx^n})={\text dy\over \text dx}\cdot ({\text d\over \text dy}\sum_{i=1}^{n+1}a_iy^i)\)

由于后面这个显然是个多项式函数,前面的我们之前求导得到为 \((y-y^2)\)

\(\therefore \displaystyle {\text d^{n+1} y\over \text dx^{n+1}}=(y-y^2)(\sum_{i=1}^{n+1}ia_iy^{i-1})=(1-y)(\sum_{i=1}^{n+1}ia_iy^i)\)

也就是说,我们可以根据之 \(n\) 阶导的 \(y\) 多项式系数,可以 \(O(n)\) 地推出 \((n+1)\) 阶导的 \(y\) 多项式系数

\(\because \displaystyle y|_{x=0}={1\over 2}\)

故我们可以 \(O(n^2)\) 地求解出问题结果


Ans[MAXN] 储存 \(k\) 阶导的答案, INV2[MAXN] 储存 \(({1\over 2})^k \mod (10^9+7)\) , Frac[MAXN] 储存 \(({1\over k!})\mod (10^9+7)\)F[MAXN] 储存当前阶导数的 \(y\) 多项式系数

可以先 \(O(n)\) 预处理出 \(({1\over 2})^k \mod (10^9+7)\)\(k!\mod (10^9+7)\)

使用欧拉定理与快速幂 \(({1\over 1000!})\equiv (1000!)^{(10^9+7)-1}(\mod 10^9+7)\) 求解出 \(({1\over 1000!})\mod (10^9+7)\) 。再反向扫回,求解出 \(({1\over k!})\mod (10^9+7)\)

接下来通过递推算出 F[MAXN] 从而求解 Ans[MAXN]

由于从递推关系可得,式子从 \(\displaystyle \sum_{i=0}^{n+1}a_iy^i\) 变为 \(\displaystyle (1-y)(\sum_{i=0}^{n+1}ia_iy^i)\)

故写出代码:

    for(int i=1;i<=1000;i++){
        for(int j=1;j<=i;j++) F[j]=j*F[j]%MOD;
        for(int j=i+1;j>=1;j--) F[j]=(F[j]-F[j-1]+MOD)%MOD;
    }

再加入刚刚求解的 INV2[MAXN],Frac[MAXN] 即可算出答案

    for(int i=1;i<=1000;i++){
        for(int j=1;j<=i;j++) F[j]=j*F[j]%MOD;
        for(int j=i+1;j>=1;j--) F[j]=(F[j]-F[j-1]+MOD)%MOD;
        for(int j=1;j<=i+1;j++) Ans[i]=(Ans[i]+F[j]*INV2[j]%MOD)%MOD;
        Ans[i]=Ans[i]*Frac[i]%MOD;
    }

最后主函数里面 \(O(1)\) 查询即可,总复杂度 \(O(n^2+T),T\) 为询问次数


【代码】

#include<iostream>
using namespace std;
typedef long long ll;
const ll MOD=1e9+7,inv2=MOD+1>>1,MAXN=1024;//inv2 为 2 的逆元
ll Ans[MAXN];
inline ll fpow(ll a,ll x){//快速幂
    ll ans=1;
    for(;x;x>>=1,a=a*a%MOD) if(x&1) ans=ans*a%MOD;
    return ans;
}
inline void pre(){
    ll F[MAXN]={0},INV2[MAXN],Frac[MAXN];
    INV2[0]=Frac[0]=1;//初始化
    for(int i=1;i<=1010;i++){//多算一点,防炸
        INV2[i]=INV2[i-1]*inv2%MOD;
        Frac[i]=i*Frac[i-1]%MOD;
    }
    Frac[1000]=fpow(Frac[1000],MOD-2);
    for(int i=999;i>=0;i--)
        Frac[i]=Frac[i+1]*(i+1)%MOD;

    Ans[0]=inv2;//初始化
    F[1]=1;//0 阶导时,y 的多项式为 y,即 1*y^1
    for(int i=1;i<=1000;i++){
        for(int j=1;j<=i;j++) F[j]=j*F[j]%MOD;
        for(int j=i+1;j>=1;j--) F[j]=(F[j]-F[j-1]+MOD)%MOD;
        for(int j=1;j<=i+1;j++) Ans[i]=(Ans[i]+F[j]*INV2[j]%MOD)%MOD;
        Ans[i]=Ans[i]*Frac[i]%MOD;
    }
}
int main(){
    pre();
    int N;
    while(cin>>N)
        cout<<Ans[N]<<endl;
    return 0;
}

法二

感谢神仙学弟 Stump 提出的方法

考虑 \(\displaystyle e^x=\sum_{i=0}^\infty {1\over n!}x^n\)

\(\displaystyle e^{-x}=\sum_{i=0}^\infty {(-1)^n\over n!}x^n\)

由于询问的最高次数 \(k\leq 1000\) 故我们直接取 \(F(x)\equiv (e^{-x}+1)\pmod {x^{1024}}\)

则多项式系数 \(F[n]\equiv ({(-1)^n\over n!}+[n==0])\pmod {10^9+7}\)

最后只要令多项式 \(G(x)\equiv F^{-1}(x)\pmod {x^{1024}}\)

暴力求解出多项式 \(G(x)\) 即可得出答案:对于每次的询问 \(k\) ,输出 \(G[k]\)

而求逆可以考虑 \(O(n^2)\) 暴力方法:

\(G(x)\equiv F^{-1}(x)\pmod x\) 时,\(G[0]\equiv (F[0])^{-1}\pmod x\)

否则求 \(G(x)\equiv F^{-1}(x)\pmod {x^n}\) 时,先递归下去,求解出 \(G(x)\equiv F^{-1}(x)\pmod {x^{n-1}}\)

而后根据式子 \(\displaystyle (G*F)[n-1]=\sum_{i=0}^{n-1}F[i]G[n-1-i]=F[0]G[n-1]+\sum_{i=1}^{n-1}F[i]G[n-1-i]\equiv 0\pmod {10^9+7}\)

\(\displaystyle G[n-1]\equiv -(F[0])^{-1}\cdot \sum_{i=1}^{n-1}F[i]G[n-1-i]\equiv -G[0]\cdot \sum_{i=1}^{n-1}F[i]G[n-1-i]\pmod {10^9+7}\)

前半段求 \(F(x)\) 用预处理阶乘的方法,时间复杂度 \(T(n)=O(n)+O(\log n)+O(n)=O(n)\)

后半段求 \(G(x)\) ,时间复杂度为 \(\displaystyle T(n)=\log n+\sum_{i=2}^n \sum_{j=1}^i 1=O(n^2)\)

故总复杂度为 \(O(n^2+T)\)

#include<iostream>
using namespace std;
typedef long long ll;
const ll MOD=1e9+7,MAXN=1024;
inline ll fpow(ll a,ll x) { ll ans=1; for(;x;x>>=1,a=a*a%MOD) if(x&1) ans=ans*a%MOD; return ans; }
inline ll inv(ll a) { return fpow(a,MOD-2); }
inline void inv(ll f[MAXN],ll g[MAXN],ll mod){
    if(mod==1){
        g[0]=inv(f[0]);
        return ;
    }
    inv(f,g,mod-1);
    for(int i=1;i<mod;i++) g[mod-1]=(g[mod-1]+f[i]*g[mod-1-i]%MOD)%MOD;
    g[mod-1]=(MOD-g[mod-1]*g[0]%MOD)%MOD;
}
ll Frac[MAXN],F[MAXN],G[MAXN];
inline void pre(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    Frac[0]=1;
    for(int i=1;i<MAXN;i++) Frac[i]=Frac[i-1]*i%MOD;
    F[MAXN-1]=inv(Frac[MAXN-1]);
    for(int i=MAXN-1;i>0;i--) F[i-1]=F[i]*i%MOD;
    for(int i=1;i<MAXN;i+=2) F[i]=MOD-F[i];
    F[0]++;
    inv(F,G,1024);
}
int main(){
    pre();
    int K;
    while(cin>>K)
        cout<<G[K]<<endl;
    return 0;
}

法三

由于已知 \(e^{-x}+1\pmod{x^{1024}}\) ,对其进行任意模数的多项式求逆即可

MTT 版

#include<iostream>
#include<cmath>
using namespace std;
typedef long long ll;
typedef long double db;
typedef pair<db,db> pdd;
#define fi first
#define se second
inline pdd operator + (const pdd &a,const pdd &b) { return pdd(a.fi+b.fi,a.se+b.se); }
inline pdd operator - (const pdd &a,const pdd &b) { return pdd(a.fi-b.fi,a.se-b.se); }
inline pdd operator * (const pdd &a,const pdd &b) { return pdd(a.fi*b.fi-a.se*b.se, a.fi*b.se+a.se*b.fi); }
inline pdd operator / (const pdd &a,db b) { return pdd(a.fi/b,a.se/b); }
inline pdd operator ~ (const pdd &p) { return pdd(p.fi,-p.se); }
inline pdd operator / (const pdd &a,const pdd &b) { return a*(~b)/(b*(~b)).fi; }

const int LimBit=12, M=1<<LimBit<<1;
const db pi=acos(-1),eps=1e-9;
int a[M], b[M];
struct FFT{
    int N,na,nb,Rev[M+M],*rev, p, m, InV[M];
    pdd W[2][M+M],*w[2];
    inline ll fpow(ll a,ll x) const { ll ans=1; for(;x;x>>=1,a=a*a%p) if(x&1) ans=ans*a%p; return ans; }
    inline ll inv(ll a) const { return fpow(a,p-2); }
    inline int add(int a,int b) const { return (a+b>=p)?(a+b-p):(a+b); }
    inline int dis(int a,int b) const { return (a-b<0)?(a-b+p):(a-b); }

    FFT(int p_):p(p_){
        InV[1]=1;
        for(int i=2;i<p&&i<M;++i)
            InV[i]=dis(p,(ll)p/i*InV[p%i]%p);

        m=sqrt(p)+1;
        w[0]=W[0]; w[1]=W[1]; rev=Rev;
        for(int d=LimBit;d>=0;--d){
            int N=1<<d;
            for(int i=0;!(i>>d);++i){
                rev[i]=(rev[i>>1]>>1)|((i&1)<<d-1);
                w[0][i]=w[1][i]=pdd( cos(2*pi*i/N) , sin(2*pi*i/N) );
                w[1][i].se=-w[1][i].se;
            }
            w[0]+=N; w[1]+=N; rev+=N;
        }
    }
    inline void work(){
        w[0]=W[0]; w[1]=W[1]; rev=Rev;
        for(int d=LimBit;1<<d>N;--d)
            w[0]+=1<<d, w[1]+=1<<d, rev+=1<<d;
    }
    inline void fft(pdd *a,int f){
        static pdd x;
        for(int i=0;i<N;++i) if(i<rev[i]) swap(a[i],a[rev[i]]);
        for(int i=1;i<N;i<<=1)
            for(int j=0,t=N/(i<<1);j<N;j+=i<<1)
                for(int k=0,l=0;k<i;++k,l+=t)
                    x=w[f][l]*a[j+k+i], a[j+k+i]=a[j+k]-x, a[j+k]=a[j+k]+x;
        if(f) for(int i=0;i<N;++i) a[i]=a[i]/N;
    }
    inline void doit(int *a,int *b,int na,int nb){
        static pdd x, y, p0[M], p1[M];
        for(N=1;N<na+nb-1;N<<=1);
        for(int i=na;i<N;++i) a[i]=0;
        for(int i=nb;i<N;++i) b[i]=0;
        for(int i=0;i<N;++i)
            p1[i]=pdd(a[i]/m,a[i]%m),p0[i]=pdd(b[i]/m,b[i]%m);
        work(); fft(p1,0); fft(p0,0);
        for(int i=0,j;i+i<=N;++i){
            j=(N-1)&(N-i);
            x=p0[i], y=p0[j];
            p0[i]=(x-(~y))*p1[i]/pdd(0,2);
            p1[i]=(x+(~y))*p1[i]/pdd(2,0);
            if(i==j) continue;
            p0[j]=(y-(~x))*p1[j]/pdd(0,2);
            p1[j]=(y+(~x))*p1[j]/pdd(2,0);
        }
        fft(p1,1); fft(p0,1);
        for(int i=0;i<N;++i)
            a[i]=((((ll)(p1[i].fi+0.5+eps)*m%p+(ll)(p1[i].se+0.5+eps)+(ll)(p0[i].fi+0.5+eps))%p*m%p+(ll)(p0[i].se+0.5+eps))%p+p)%p;
    }
    inline void get_inv(int *f,int *g,int n){
        for(int i=1;i<n;++i) g[i]=0; g[0]=inv(f[0]);
        for(int l=1;l<n;l<<=1){
            for(int i=l;i<l<<1;++i) g[i]=0;
            for(int i=0;i<l<<1;++i) a[i]=f[i];
            doit(a,g,l<<1,l<<1);
            for(int i=0;i<l<<1;++i) a[i]=dis(0,a[i]); a[0]=add(a[0],2);
            doit(g,a,l<<1,l<<1);
        }
        for(int i=n;i<N;++i) g[i]=0;
    }
}*fft;

int n,p=1e9+7,f[M],g[M];
inline void pre(){
    f[0]=1;
    for(int i=1;i<1024;++i)
        f[i]=(ll)f[i-1]*i%p;
    f[1023]=fft->inv(f[1023]);
    for(int i=1023;i;--i)
        f[i-1]=(ll)f[i]*i%p;
    for(int i=1;i<1024;i+=2)
        f[i]=fft->dis(0,f[i]);
    ++f[0];
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    fft=new FFT(p);
    pre();

    fft->get_inv(f,g,1024);
    while(cin>>n) cout<<g[n]<<"\n";

    cout.flush();
    return 0;
}

三模 NTT 版

posted @ 2020-07-14 22:23  JustinRochester  阅读(321)  评论(6编辑  收藏  举报