CF 622F (拉格朗日插值)
阿新 • • 發佈:2018-11-24
解題思路
比較經典的一道題目。第一種方法是差分,就是假設\(k=3\),我們打一個表。
0 1 9 36 100 225
1 8 27 64 125
7 19 37 61
12 18 24
6 6
表中第一行為所要求的字首和,後面的\(f[i][j]=f[i-1][j]+f[i-1][j-1]\),就是每個數字等於上面的數字\(-\)左上的數字,減到最後發現只剩一樣的數字。此時第一行的後面的所有數字只與後面每一行的第一個數字有關,而且係數恰好是一個組合數。比如說我們要算第一行第\(10\)個數字,也就是\(n=10\)的時候的答案。那麼
\[ ans=C(10,1)*1+C(10,2)*7+C(10,3)*12+C(10,4)*6 \]
這個的證明可以根據實際意義來,從第二行第一個數字到第一行第十個數字一共走\(10\)步,期中\(1\)步向上走,那麼就為\(C(10,1)\)。
所以我們暴力算出前\(k+1\)項的值,然後遞推即可。差分法的時間複雜度為\(O(k^2)\)的,無法通過此題。但是這個思想值得學習。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #define int long long using namespace std; const int MAXN = 1005; const int MOD = 1e9+7; int n,k,inv[MAXN],fac[MAXN]; int f[MAXN][MAXN],ans; inline int fast_pow(int x,int y){ int ret=1; for(;y;y>>=1){ if(y&1) ret=ret*x%MOD; x=x*x%MOD; } return ret; } inline int C(int n,int m){ return fac[n]*inv[m]%MOD*inv[n-m]%MOD; } signed main(){ scanf("%lld%lld",&n,&k);fac[0]=1; for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%MOD;inv[n]=fast_pow(fac[n],MOD-2); for(int i=n-1;~i;i--) inv[i]=inv[i+1]*(i+1)%MOD; for(int i=1;i<=k+1;i++) f[0][i]=f[0][i-1]+fast_pow(i,k),f[0][i]%=MOD; if(n<=k+1) {printf("%lld\n",f[0][n]);return 0;} for(int i=1;i<=k+1;i++) for(int j=i;j<=k+1;j++) f[i][j]=f[i-1][j]-f[i-1][j-1],f[i][j]=(f[i][j]+MOD)%MOD; for(int i=1;i<=k+1;i++) ans=(ans+f[i][i]*C(n,i)%MOD)%MOD; printf("%lld\n",ans); return 0; }
第二種方法自然就是拉格朗日插值法,對於一段連續的值來說,拉格朗日插值法可以在\(O(k)\)內解決,具體來說就是將定義式變形。
\[ \prod\limits_{i!=j} \frac{x-x_j}{x_i-x_j} \]
因為帶入的值是連續的,所以上面一定是兩段連續的乘積,我們要維護一個字首乘積字尾乘積拼起來。下面的話可以看成兩個階乘,正負取決於\(n-i\)的奇偶性,這樣就可以\(O(n)\)的算出結果。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #define int long long using namespace std; const int MAXN = 1000005; const int MOD = 1e9+7; int n,k,inv[MAXN],y[MAXN]; int pre[MAXN],suf[MAXN],ans,fac[MAXN]; int fast_pow(int x,int y){ int ret=1; for(;y;y>>=1){ if(y&1) ret=ret*x%MOD; x=x*x%MOD; } return ret; } signed main(){ scanf("%lld%lld",&n,&k); pre[0]=1;for(int i=1;i<=k+2;i++) pre[i]=pre[i-1]*(n-i)%MOD; suf[k+3]=1;for(int i=k+2;i;i--) suf[i]=suf[i+1]*(n-i)%MOD; fac[0]=1;for(int i=1;i<=k+2;i++) fac[i]=fac[i-1]*i%MOD; inv[k+2]=fast_pow(fac[k+2],MOD-2); for(int i=k+1;~i;i--) inv[i]=inv[i+1]*(i+1)%MOD;int s1,s2; for(int i=1;i<=k+2;i++) y[i]=(y[i-1]+fast_pow(i,k))%MOD; for(int i=1;i<=k+2;i++){ s1=pre[i-1]*suf[i+1]%MOD; s2=inv[i-1]*inv[k+2-i]*(((k+2-i)&1)?-1:1)%MOD; ans=((ans+s1*s2%MOD*y[i]%MOD)%MOD+MOD)%MOD; } printf("%lld\n",ans); return 0; }