[省选联考 2020 A 卷] 组合数问题 题解报告
题目要求
\[\sum_{k=0}^nf(k)\times x^k\times\dbinom{n}{k}
\]
我觉得 ix35 的方法还是蛮不错的,其他的斯特林数推导和组合意义 dp 都给我看傻了
首先考虑将 \(f\) 拆成单项式有
\[\sum_{i=0}^ma_i\times k^i\times \dbinom{n}{k}
\]
由 \(k\times \dbinom{n}{k}=n\times \dbinom{n-1}{k-1}\) 向外扩展
把 \(k^2\times \dbinom{n}{k},k^3\times\dbinom{n}{k}\dots\) 手算一遍
可以发现对于 \(k^p\times \dbinom{n}{k}\) 有一般形式
\[=\sum_{i=1}^pS(p,j)\times n^{\underline{j}}\times\dbinom{n-j}{k-j}
\]
并且对于 \(S(p,j)=S(i-1,j)\times j+S(i-1,j-1)\) 存在这个递归式
这个时候回带到原式中
\[\sum_{k=0}^{n}x^k\left(a_0+\sum_{i=1}^ma_i\times k^i\times \dbinom{n}{k}\right)
\]
务必要特殊讨论 \(a_0\) 的存在,否则你会因为这个死活过不去大样例。
把关于 n 的求和符号换进去,可以得到这样一个式子
\[\sum_{k=0}^nx^k\sum_{j=1}^i
S(i,j)\times n^{\underline{j}}\times\dbinom{n-j}{k-j}
\]
进一步交换
\[\sum_{j=1}^i
S(i,j)\times n^{\underline{j}}\times\sum_{k=0}^nx^k\dbinom{n-j}{k-j}
\]
单独考虑后面那个求和符号,等价于
\[x^j\sum_{k=0}^{n-j}x\dbinom{n-j}{k}
\]
再用一步二项式定理(一定要带着那个 \(x^j\),不然还是只能过第一个样例
\[x^j\times(1+x)^{n-j}
\]
整理一下
\[a_0(1+x)^n+\sum_{i=1}^ma_i\left(\sum_{j=1}^iS(i,j)\times n^{\underline{j}}\times x^j \times(1+x)^{n-j}\right)
\]
然后这个一个依赖 \(n\) 的算法被我们改造成了依赖 \(m\) 的算法,复杂度是 \(O(m^2\log 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("\n");
}
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\n",Ans);
}
// Ans += ((n+1) * n* a[0])%p;
// Ans %= p;
printf("%lld",Ans);
}
以后遇到这样的数学推导题,经过一遍草率的推导得到了最终式子的时候,一定要从头认真推导一遍,每一个项都不能忽略