[省选联考 2020 A 卷] 组合数问题
\(\text{Problem}:\)[省选联考 2020 A 卷] 组合数问题
\(\text{Solution}:\)
前言:[清华集训2016] 如何优雅地求和 的弱化版。
由二项式定理,有 \(\sum\limits_{k=0}^{n}x^{k}\binom{n}{k}=(x+1)^{n}\)。这提示我们想要改变 \(f(k)\),使得式子中出现 \(\sum x^k\binom{n}{k}\) 的形式。那么可以将普通多项式转下降幂多项式,设 \(f(k)=b_{0}+b_{1}k^{\underline{1}}+b_{2}k^{\underline{2}}+\cdot\cdot\cdot +b_{m}k^{\underline{m}}\),有:
对于第二步转化,将 \(k^{\underline{i}}\) 和 \(\binom{n}{k}\) 都展开,\(k!\) 抵消后即可。
现在考虑改变枚举顺序:
将 \(k\) 用 \(k-i\) 代替,提取 \(x^{i}\),可以得到:
终于出现了我们想要的形式,利用二项式定理,得到:
在已知序列 \(b\) 的情况下,可以在 \(O(m\log x)\) 的时间内求出答案。问题转化为将普通多项式改为下降幂多项式。
引理:\(x^n=\sum\limits_{i=0}^{n}{n\brace i}x^{\underline{i}}\)。
考虑 \(x^n\) 的组合意义:把 \(n\) 个物体随意放在 \(x\) 个位置。枚举 \(x\) 个位置中哪些被选择,有:
第二类斯特林数:将 \(n\) 个不同物体划分成 \(k\) 个集合的方案数。由于位置有顺序关系,所以还要乘上 \(i!\)。于是可以得到上式,证毕。
对于多项式 \(f(k)=a_{0}+a_{1}k+a_{2}k^2+\cdot\cdot\cdot+a_{m}k^m\),考虑展开 \(k^{i}\),得到:
那么预处理第二类斯特林数,暴力给 \(x^{\underline{j}}\) 加上对应的系数即可。总时间复杂度 \(O(m^2)\)。
对于 [清华集训2016] 如何优雅地求和 这题,做法完全相同。但是 \(m\) 范围略大,需要多项式科技(也有大力推式子得到 \(O(m)\) 递推的做法,此处不表)。
\(\text{Code}:\)
#include <bits/stdc++.h>
#pragma GCC optimize(3)
//#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=1010;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
return s*w;
}
int n,X,Mod,m,a[N],b[N],S[N][N],res;
inline int ksc(int x,int p) { int res=1; for(;p;p>>=1, x=1ll*x*x%Mod) if(p&1) res=1ll*res*x%Mod; return res; }
signed main()
{
n=read(), X=read(), Mod=read(), m=read();
for(ri int i=0;i<=m;i++) a[i]=read();
S[0][0]=1;
for(ri int i=1;i<=m;i++)
{
for(ri int j=1;j<=i;j++)
{
S[i][j]=(S[i-1][j-1]+1ll*j*S[i-1][j]%Mod)%Mod;
}
}
for(ri int i=0;i<=m;i++)
{
for(ri int j=0;j<=i;j++)
{
b[j]=(b[j]+1ll*a[i]*S[i][j]%Mod)%Mod;
}
}
int now=1;
for(ri int i=0;i<=min(n,m);i++)
{
res=(res+1ll*b[i]*now%Mod*ksc(X,i)%Mod*ksc(X+1,n-i)%Mod)%Mod;
now=1ll*now*(n-i)%Mod;
}
printf("%d\n",res);
return 0;
}