BZOJ.5339.[TJOI2018]教科書般的褻瀆(拉格朗日插值)
阿新 • • 發佈:2019-01-09
題意的一點說明:
\(k\)次方這個\(k\)是固定的,也就是最初需要多少張褻瀆,每次不會改變;
因某個怪物死亡引發的褻瀆不會計分。
不難發現當前所需的張數是空格數+1,即\(m+1\)。
貢獻不妨寫成:\(\sum_{i=1}^ni^{m+1}-\sum_{i=1}^mA_i^{m+1}\)。注意此時的\(A_i\)是剩下的空格(具體看程式碼最底下的暴力部分吧)。
所以問題在於求\(\sum_{i=1}^ni^{m+1}\)。自然數冪和有很多種求法。
這裡寫插值做法:
\(\sum_{i=1}^ni^{m}\)是一個以\(n\)為自變數的\(m+1\)次多項式,我們代入\(m+2\)
代入點的\(x\)值連續,就可以用字首積、字尾積和階乘將拉格朗日插值優化到\(O(n)\)計算單點函式值(具體見下面)。
複雜度\(O(Tm^2)\)。
寫一下拉格朗日插值的式子及實現(具體見這裡或這裡):
\[f(x)=\sum_{i=0}^ny_i\prod_{j\neq i}\frac{x-x_i}{x_i-x_j}\]
當\(x_i\)取\(i\)時:
\[f(x)=\sum_{i=0}^ny_i\prod_{j\neq i}\frac{x-i}{i-j}\]
對於\(x\),預處理字首積、字尾積:\(pre_j=\prod_{i=0}^jx-x_i,\;suf_j=\prod_{i=j}^nx-x_i\)
不難發現上面式子的分子就是字首積乘字尾積,分母是兩個階乘相乘(注意會有符號問題)。所以就是:
\[f(x)=\sum_{i=0}^ny_i\frac{pre_{i-1}\times suf_{i+1}}{i!\times(n-i)!}\]
就可以\(O(n)\)計算\(f(x)\)了。
//824kb 36ms #include <cstdio> #include <algorithm> #define mod 1000000007 #define Mod(x) x>=mod&&(x-=mod) #define Add(x,v) (x+=v)>=mod&&(x-=mod) #define Add2(x,y) (x+y>=mod?x+y-mod:x+y) typedef long long LL; const int N=55; int ifac[N],y[N]; LL A[N]; inline int FP(int x,int k) { int t=1; for(; k; k>>=1,x=1ll*x*x%mod) if(k&1) t=1ll*t*x%mod; return t; } int F(const int x,const int m)//∑_{i=1}^x i^m 自變數為x的m+1次多項式 在x處的取值 { static int pre[N],suf[N]; const int lim=m+1; pre[0]=x, suf[lim+1]=1, Mod(suf[lim]); for(int i=1; i<=m; ++i) pre[i]=1ll*pre[i-1]*(x+mod-i)%mod; for(int i=lim; i; --i) suf[i]=1ll*suf[i+1]*(x+mod-i)%mod; LL ans=0; for(int i=0,up,down; i<=lim; ++i) { if(i) up=1ll*pre[i-1]*suf[i+1]%mod*y[i]%mod; else up=1ll*suf[i+1]*y[i]%mod; down=(lim-i)&1?mod-1ll*ifac[i]*ifac[lim-i]%mod:1ll*ifac[i]*ifac[lim-i]%mod; ans+=1ll*up*down%mod; } return ans%mod; } int main() { ifac[N-1]=956708188; for(int i=N-1; i; --i) ifac[i-1]=1ll*ifac[i]*i%mod; int T; for(scanf("%d",&T); T--; ) { LL n; int m; scanf("%lld%d",&n,&m), n%=mod; for(int i=1; i<=m; ++i) scanf("%lld",&A[i]); std::sort(A+1,A+1+m); for(int i=1; i<=m; ++i) A[i]%=mod;//sort後再取模! LL ans=0; ++m, y[0]=0; for(int i=1; i<=m+1; ++i) y[i]=y[i-1]+FP(i,m), Mod(y[i]); for(int t=0; t<m; ++t) { ans+=F(Add2(n,mod-A[t]),m);// for(int i=1; i<=n; ++i) ans+=FP(i,m); for(int i=t; i<m; ++i) ans-=FP(Add2(A[i],mod-A[t]),m); } printf("%lld\n",(ans%mod+mod)%mod); } return 0; } //BruteForce: // ++m; // for(int t=1; t<=m; ++t) // { // for(int i=1; i<=n; ++i) ans+=FP(i,m); // for(int i=t; i<m; ++i) ans-=FP(A[i],m); // for(int i=t+1; i<m; ++i) A[i]-=A[t]; // n-=A[t]; // }