[省选联考 2020 A 卷] 组合数问题 题解报告

题目要求

[sum_{k=0}^nf(k) imes x^k imesdbinom{n}{k} ]

我觉得 ix35 的方法还是蛮不错的,其他的斯特林数推导和组合意义 dp 都给我看傻了

首先考虑将 (f) 拆成单项式有

[sum_{i=0}^ma_i imes k^i imes dbinom{n}{k} ]

(k imes dbinom{n}{k}=n imes dbinom{n-1}{k-1}) 向外扩展

(k^2 imes dbinom{n}{k},k^3 imesdbinom{n}{k}dots) 手算一遍

可以发现对于 (k^p imes dbinom{n}{k}) 有一般形式

[=sum_{i=1}^pS(p,j) imes n^{underline{j}} imesdbinom{n-j}{k-j} ]

并且对于 (S(p,j)=S(i-1,j) imes j+S(i-1,j-1)) 存在这个递归式

这个时候回带到原式中

[sum_{k=0}^{n}x^kleft(a_0+sum_{i=1}^ma_i imes k^i imes dbinom{n}{k} ight) ]

务必要特殊讨论 (a_0) 的存在,否则你会因为这个死活过不去大样例。

把关于 n 的求和符号换进去,可以得到这样一个式子

[sum_{k=0}^nx^ksum_{j=1}^i S(i,j) imes n^{underline{j}} imesdbinom{n-j}{k-j} ]

进一步交换

[sum_{j=1}^i S(i,j) imes n^{underline{j}} imessum_{k=0}^nx^kdbinom{n-j}{k-j} ]

单独考虑后面那个求和符号,等价于

[x^jsum_{k=0}^{n-j}xdbinom{n-j}{k} ]

再用一步二项式定理(一定要带着那个 (x^j),不然还是只能过第一个样例

[x^j imes(1+x)^{n-j} ]

整理一下

[a_0(1+x)^n+sum_{i=1}^ma_ileft(sum_{j=1}^iS(i,j) imes n^{underline{j}} imes x^j imes(1+x)^{n-j} ight) ]

然后这个一个依赖 (n) 的算法被我们改造成了依赖 (m) 的算法,复杂度是 (O(m^2log m))

#include<bits/stdc++.h>

using namespace std;

#define int long long
#define pii pair<int,int>

template<typename _T>
inline void read(_T &x)
{
	x=0;char s=getchar();int f=1;
	while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
	x*=f;
}

const int np = 1e3 + 5;

int a[np];
int x,n,m,p;
int s[np][np];
int dfac[np];
int x_[np];

inline int power(int a,int b)
{
	int res(1);
	while(b)
	{
		if(b & 1) res = res * a,res %= p;
		a = a * a ;
		a %= p;
		b >>= 1;
	}
	return res;
}

signed main()
{
	read(n),read(x),read(p),read(m);
	for(int i=0;i <=m;i++) read(a[i]);
	for(int i=1;i <= 1000;i ++)
	{
		s[i][1] = 1;
		for(int j=2;j <= i;j++)
		{
			s[i][j] = s[i-1][j]*j+s[i-1][j-1];
			s[i][j] %= p;
		}
		// for(int j=1;j<=i;j++) printf("%lld ",s[i][j]);
		// printf("
");		
	}

	dfac[0] =1 ;
	for(int i=1;i <= 1000;i ++)
	{
		dfac[i] = dfac[i-1]*(n-i+1);
		dfac[i] %= p;
	}

	int Ans =power(1+x,n)*a[0];
	Ans %= p;
	for(int i=1;i <= m;i ++)
	{
		int qAns = 0;
		for(int j=1;j <= i;j ++)
		{
			qAns += ((((s[i][j] * dfac[j])%p) * power(x,j)%p) *power(1 + x,n-j))%p;
			qAns %= p;
		}
		qAns *= a[i];
		qAns %= p;
		Ans += qAns;
		Ans %= p;
		// printf("%lld
",Ans);
	}

	// Ans  += ((n+1) * n* a[0])%p;
	// Ans %= p;
	printf("%lld",Ans);


}

以后遇到这样的数学推导题,经过一遍草率的推导得到了最终式子的时候,一定要从头认真推导一遍,每一个项都不能忽略

原文地址:https://www.cnblogs.com/-Iris-/p/15340268.html