BZOJ2655 calc
题意
一个序列 \(a_1,\cdots,a_n\) 是合法的,当且仅当:
- 长度为给定的 \(n\)
- \(a_1,\cdots,a_n\) 都是 \([1,A]\) 中的整数
- \(a_1,\cdots,a_n\) 互不相等
一个序列的值定义为它里面所有数的乘积, 即 \(\Pi_{i=1}^n a_i\)
求所有不同合法序列的值的和
两个序列不同当且仅当他们任意一位不一样
输出答案对一个数 mod 取余的结果
题解
显然求的是
\[ [x^n]\Pi_{i=1}^A(ix+1) \]
由于 \(A\) 很大,不能直接求,于是令 \(f_{i,j}\) 表示 \([x^j]\Pi_{k=1}^i(kx+1)\)
易得
\[ f_{i,j}=f_{i-1,j-1}\times j+f_{i-1,j} \]
然后展开 \(f_{i-1,j}\), 一直展开
得到
\[ f_{i,j}=\sum_{k=0}^{i-1}(k+1)f_{k,j-1} \]
这说明 \(f_{i,j}\) 可以表示为一个多项式
其中 \(f_{i,j}\) 的次数是 \(f_{i,j-1}\) 的次数加 2,因为一个是前缀和的次数,一个是乘系数的次数
然后拉格朗日插值算第 \(A\) 项就好了
#include<cstdio>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
template<typename T>inline void rd(T&x){int fl=0,ch;while(ch=getchar(),ch<48||57<ch)fl^=!(ch^45);x=(ch&15);while(ch=getchar(),47<ch&&ch<58)x=x*10+(ch&15);if(fl)x=-x;}
template<typename T>inline void pt(T x){if(x<0)putchar('-'),x=-x;if(x>9)pt(x/10);putchar(x%10+48);}
template<typename T>inline void pt(T x,int ch){pt(x),putchar(ch);}
template<typename T>inline T max(const T&x,const T&y){return x<y?y:x;}
template<typename T>inline T min(const T&x,const T&y){return x<y?x:y;}
const int N=2005;
int A,n,P,f[N][N],inv[N];
int main(){
rd(A),rd(n),rd(P);
inv[0]=inv[1]=1;
for(int i=2;i<N;++i)inv[i]=1ll*(P-P/i)*inv[P%i]%P;
f[1][0]=f[1][1]=1;
for(int i=2;i<=n*2+1;++i){
f[i][0]=1;
for(int j=1;j<=n;++j)
f[i][j]=(1ll*i*f[i-1][j-1]%P+f[i-1][j])%P;
}
int ans=0;
for(int i=1;i<=n*2+1;++i){
int x=f[i][n];
for(int j=1;j<=n*2+1;++j)if(i^j)x=1ll*x*((A-j+P)%P)%P,x=1ll*x*(i>j?inv[i-j]:P-inv[j-i])%P;
(ans+=x)%=P;
}
for(int i=1;i<=n;++i)ans=1ll*ans*i%P;
printf("%d\n",ans);
return 0;
}