1. 程式人生 > >洛谷P4451 [國家集訓隊]整數的lqp拆分 [生成函數]

洛谷P4451 [國家集訓隊]整數的lqp拆分 [生成函數]

mat mod 語文 inline code 時間 n) 快速 long long

傳送門

題意簡述:語文不好不會寫,自己看吧

思路如此精妙,代碼如此簡潔,實是鍛煉思維水經驗之好題


這種題當然是一眼DP啦。

\(dp_n\)為把\(n\)拆分後的答案。為了方便我們設\(dp_0=1\)

由題意有
\[ dp_n=[n=0]+\sum_{i=1}^n dp_{n-i}f_i \]
按照套路,我們考慮它的生成函數\(A(x)\)
\[ \begin{align*} A(x)&=\sum_n ([n=0]+\sum_{i=1}^n f_i dp_{n-i})x^n\&=1+\sum_{n=1}^{\infty} (\sum_{i=1}^n f_i dp_{n-i})x^n\&=1+\sum_n (\sum_{i=0}^n f_i dp_{n-i})x^n//多枚舉一點不會造成影響\&=1+A(x)F(x) \end{align*} \]


我們知道\(F(x)=\frac{x}{1-x-x^2}\),所以有
\[ \begin{align*} A(x)&=\frac{1}{1-F(x)}\&=1+\frac{x}{1-2x-x^2}\&=1+\frac{1}{2\sqrt{2}}(\frac{1}{1-(1+\sqrt{2})x}-\frac{1}{1-(1-\sqrt{2})x})\&=1+\sum_n \frac{1}{2\sqrt{2}}[(1+\sqrt{2})^n-(1-\sqrt{2})^n] \end{align*} \]
所以
\[ Ans_n=\frac{(1+\sqrt{2})^n-(1-\sqrt{2})^n}{2\sqrt{2}} \]

發現題目要求\(Ans_n\pmod{1e9+7}\),枚舉可得$59713600^2 \equiv 2\pmod{1e9+7} $ ,所以\(\sqrt{2}\equiv 59713600 \pmod{1e9+7}\)

於是我們在\(O(\log n)\)的時間內求得了答案!

問題來了:為什麽數據範圍只有\(10^6\)呢?我\(O(\log n)\)快速冪還沒有\(O(n)\)遞推快……

代碼:

#include<bits/stdc++.h>
namespace my_std{
    using namespace std;
    #define pii pair<int,int>
    #define fir first
    #define sec second
    #define MP make_pair
    #define rep(i,x,y) for (int i=(x);i<=(y);i++)
    #define drep(i,x,y) for (int i=(x);i>=(y);i--)
    #define go(x) for (int i=head[x];i;i=edge[i].nxt)
    #define sz 101010
    #define mod (int)(1e9+7)
    typedef long long ll;
    template<typename T>
    inline void read(T& t)
    {
        t=0;char f=0,ch=getchar();
        double d=0.1;
        while(ch>‘9‘||ch<‘0‘) f|=(ch==‘-‘),ch=getchar();
        while(ch<=‘9‘&&ch>=‘0‘) t=t*10+ch-48,ch=getchar();
        if(ch==‘.‘)
        {
            ch=getchar();
            while(ch<=‘9‘&&ch>=‘0‘) t+=d*(ch^48),d*=0.1,ch=getchar();
        }
        t=(f?-t:t);
    }
    template<typename T,typename... Args>
    inline void read(T& t,Args&... args){read(t); read(args...);}
    void file()
    {
        #ifndef ONLINE_JUDGE
        freopen("a.txt","r",stdin);
        #endif
    }
//  inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std;

const ll sqrt2=59713600;
ll ksm(ll x,int y)
{
    ll ret=1;
    for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;
    return ret;
}
ll inv(ll x){return ksm(x,mod-2);}
int n;

int main()
{
    file();
    read(n);
    cout<<(ksm(1+sqrt2,n)-ksm(mod+1-sqrt2,n)+mod)%mod*inv(2*sqrt2)%mod;
}

洛谷P4451 [國家集訓隊]整數的lqp拆分 [生成函數]