提供一个不用下降幂也不用斯特林数的做法
【分析】
(egin{aligned} &sum_{k=0}^n f(k)cdot x^k cdot inom n k \\=&sum_{k=0}^n sum_{i=0}^m a_ik^icdot x^k cdot inom n k \\=&sum_{i=0}^m a_i(sum_{k=0}^n k^icdot x^k cdot inom n k) end{aligned})
记 (displaystyle g_t=sum_{k=0}^n k^tcdot x^k cdot inom n k) ,结果显然为 (displaystyle sum_{i=0}^m a_ig_i)
现考虑如何求解 (g_t)
(egin{aligned} &sum_{k=0}^n k^tcdot x^k cdot inom n k \\=&sum_{k=0}^n k^{t-1}kcdot x^{k-1} cdot xcdot inom n k \\=&xcdot { ext dover ext dx}(sum_{k=0}^n k^{t-1}cdot x^kcdot inom n k) end{aligned})
记 (p(x)=x+1) 则 (g_0=p^n(x)) 且 (displaystyle g_t=xcdot { ext dover ext dx}g_{t-1})
考虑到求导的线性性质,有:
(egin{aligned} &xcdot { ext dover ext dx}f_np^n(x) \\=&f_ncdot [p(x)-1]cdot np^{n-1}(x) \\=&nf_ncdot p^n(x)-nf_ncdot p^{n-1}(x) end{aligned})
而 (g_t) 本质上就是一个关于 (p(x)) 的多项式,只要将求导法则重定义为上述求导法则即可
故初始化多项式为 (f_n=1) ,之后每次都按上述法则求导,再求值,即可求出 (g_0)~(g_t)
但 (n) 非常大,无法直接存下。考虑到初始时最低次项为 (p^n(x)) ,求导一次后最低次项为 (p^{n-1}(x)) ,迭代可得,求导 (m) 次后最低次项为 (p^{n-m}(x))
由于 (mleq 10^3) ,所以直接开 (O(m)) 的空间,计算的时候偏移 ((n-m)) 即可;同时,每次计算的时候从 ((n-m)) 次方开始带值即可
故总复杂度为 (O(m^2))
【代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
typedef double db;
#define fi first
#define se second
const int MAXN=1024;
int n, x, p, m, powit[MAXN], a[MAXN], f[MAXN], g[MAXN];
inline ll fpow(ll a,ll x) { ll ans=1; for(;x;x>>=1,a=a*a%p) if(x&1) ans=ans*a%p; return ans; }
inline void init() {
cin>>n>>x>>p>>m;
powit[0]=fpow(x+1, n-m);
for(int i=1; i<=m; ++i)
powit[i]=(ll)powit[i-1]*(x+1)%p;
f[m]=1;
g[0]=powit[m];
for(int t=1; t<=m; ++t) {
f[0]=0;
for(int i=1; i<=m; ++i)
f[i-1]=(f[i-1]-(f[i]=(ll)f[i]*(n-m+i)%p))%p;
for(int i=0; i<=m; ++i)
g[t]=(g[t]+(ll)f[i]*powit[i])%p;
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
init();
int res=0;
for(int i=0; i<=m; ++i)
cin>>a[i], res=(res+(ll)a[i]*g[i])%p;
res=(res+p)%p;
cout<<res;
cout.flush();
return 0;
}
运算过程中,只要保证运算结果在 ((-p,p)) 范围内,最后统一转入 (Z_p) 范围内