【2020全国统一省选】组合数问题 题解

题目大意

  求
∑ k = 0 n f ( k ) × x k × ( n k ) ( m o d p ) \sum_{k=0}^n f(k) \times x^k \times \binom{n}{k} \pmod p k=0nf(k)×xk×(kn)(modp)

  其中 n , x , p n,x,p n,x,p 为给定整数, f ( k ) f(k) f(k) 为给定多项式 f ( k ) = ∑ i = 0 m a i k i f(k)=\sum_{i=0}^m a_ik^i f(k)=i=0maiki

   n , x , p , a i ≤ 1 0 9 ,    m ≤ min ⁡ ( n , 1000 ) n,x,p,a_i \le 10^9,\ \ m \le \min(n,1000) n,x,p,ai109,  mmin(n,1000)
  1s

\\
\\
\\

题解

  转了一圈,这题的推法似乎多种多样啊。。。(虽然本质相同
  就记录一个我觉得比较好的吧。

  核心思路就是 k i ( n k ) k^i\binom{n}{k} ki(kn) 写成下降幂多项式,然后发现有奇效。

  设 f ( k ) = ∑ i = 0 m a i k i f(k)=\sum_{i=0}^m a_ik^i f(k)=i=0maiki 转化成下降幂多项式为
f ( k ) = ∑ i = 0 m b i k i ‾ = ∑ i = 0 m b i k ! ( k − i ) ! f(k)=\sum_{i=0}^m b_i k^{\underline i} = \sum_{i=0}^m b_i \frac{k!}{(k-i)!} f(k)=i=0mbiki=i=0mbi(ki)!k!

  代入原式有
∑ k = 0 n f ( k ) × x k × ( n k ) = ∑ k = 0 n ∑ i = 0 m b i k ! ( k − i ) ! × x k × n ! k ! ( n − k ) ! = ∑ k = 0 n ∑ i = 0 m b i × x k × n ! ( n − k ) ! ( k − i ) ! = ∑ k = 0 n ∑ i = 0 m b i n ! ( n − i ) ! × x k × ( n − i n − k ) = ∑ i = 0 m b i n ! ( n − i ) ! ∑ k = 0 i x k ( n − i n − k ) = ∑ i = 0 m b i n i ‾ ( x + 1 ) n − i \begin{aligned} \sum_{k=0}^n f(k) \times x^k \times \binom{n}{k} &= \sum_{k=0}^n\sum_{i=0}^m b_i \frac{k!}{(k-i)!} \times x^k \times \frac{n!}{k!(n-k)!} \\ &= \sum_{k=0}^n\sum_{i=0}^m b_i \times x^k \times \frac{n!}{(n-k)!(k-i)!} \\ &= \sum_{k=0}^n\sum_{i=0}^m b_i \frac{n!}{(n-i)!} \times x^k \times \binom{n-i}{n-k} \\ &= \sum_{i=0}^m b_i \frac{n!}{(n-i)!} \sum_{k=0}^i x^k \binom{n-i}{n-k} \\ &= \sum_{i=0}^m b_i n^{\underline i} (x+1)^{n-i} \end{aligned} k=0nf(k)×xk×(kn)=k=0ni=0mbi(ki)!k!×xk×k!(nk)!n!=k=0ni=0mbi×xk×(nk)!(ki)!n!=k=0ni=0mbi(ni)!n!×xk×(nkni)=i=0mbi(ni)!n!k=0ixk(nkni)=i=0mbini(x+1)ni

  这个东西 O ( m ) O(m) O(m) 循环一遍就好了, ( x + 1 ) n − i (x+1)^{n-i} (x+1)ni 可以预处理也可以快速幂。

  至于 b i b_i bi 的求法,也就是多项式转下降幂多项式,根据
x i = ∑ j = 0 i S ( i , j ) x j ‾ x^i = \sum_{j=0}^i S(i,j) x^{\underline j} xi=j=0iS(i,j)xj

  (其中 S ( i , j ) S(i,j) S(i,j) 表示第二类斯特林数)可得
∑ i = 0 m a i x i = ∑ i = 0 m a i ∑ j = 0 i S ( i , j ) x j ‾ = ∑ j = 0 x j ‾ ∑ i = j m a i S ( i , j ) \begin{aligned} \sum_{i=0}^m a_ix^i &= \sum_{i=0}^m a_i \sum_{j=0}^i S(i,j) x^{\underline j} \\ &= \sum_{j=0} x^{\underline j} \sum_{i=j}^m a_i S(i,j) \end{aligned} i=0maixi=i=0maij=0iS(i,j)xj=j=0xji=jmaiS(i,j)

  因此预处理第二类斯特林数,然后 b j = ∑ i = j m a i S ( i , j ) b_j= \sum_{i=j}^m a_iS(i,j) bj=i=jmaiS(i,j) 即可。

代码

#include
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

typedef long long LL;

const int maxm=1005;

LL n,x,mo,a[maxm];
int m;

LL S[maxm][maxm];
void S_pre(int n)
{
	fo(i,0,m)
	{
		fo(j,1,i-1) S[i][j]=(S[i-1][j-1]+j*S[i-1][j])%mo;
		S[i][i]=1;
	}
}

LL b[maxm];
void FFP()
{
	fo(i,0,m)
		fo(j,i,m) (b[i]+=a[j]*S[j][i])%=mo;
}

LL Pow(LL x,LL y)
{
	LL re=1;
	for(; y; y>>=1, x=x*x%mo) if (y&1) re=re*x%mo;
	return re;
}

int main()
{
	freopen("problem.in","r",stdin);
	freopen("problem.out","w",stdout);
	
	scanf("%lld %lld %lld %d",&n,&x,&mo,&m);
	fo(i,0,m) scanf("%lld",&a[i]);
	
	S_pre(m);
	FFP();
	
	LL ndown=1, xpow=1, ans=0;
	fo(i,0,m)
	{
		(ans+=b[i]*xpow%mo*ndown%mo*Pow(x+1,n-i))%=mo;
		(xpow*=x)%=mo;
		(ndown*=n-i)%=mo;
	}
	
	printf("%lld\n",ans);
}

你可能感兴趣的:(算法_数学)