题目描述
一个序列a_1,cdots,a_na1,⋯,an是合法的,当且仅当:
长度为给定的nn。
a_1,cdots,a_na1,⋯,an都是[1,A][1,A]中的整数。
a_1,cdots,a_na1,⋯,an互不相等。
一个序列的值定义为它里面所有数的乘积,即a_1 imes a_2 imescdots imes a_na1×a2×⋯×an。
求所有不同合法序列的值的和。
两个序列不同当且仅当他们任意一位不一样。
输出答案对一个数modmod取余的结果。
题解
- 我们可以先统计出数值递增的贡献和,为贡献和*n!
- 设f[i][j]表示前i个数选的值都小于等于j的贡献和,转移为f[i][j]=f[i-1][j-]*j+f[i][j-1]
- 然后呢我们就可以暴力做拉格朗日插值法就好了
代码
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #define ll long long 5 int a,n,mo,res,m,f[510][1010],y[1010]; 6 using namespace std; 7 int ksm(int a,int b) { int r=1; for (;b;b>>=1,a=1ll*a*a%mo) if (b&1) r=1ll*r*a%mo; return r; } 8 int change(int x) 9 { 10 if (x>=1&&x<=m) return y[x]; 11 int r=0; 12 for (int i=1;i<=m;i++) 13 { 14 int p=y[i],q=1; 15 for (int j=1;j<=m;j++) if (i!=j) p=1ll*p*(x-j)%mo,q=1ll*q*(i-j)%mo; 16 p<0?p+=mo:0,q<0?q+=mo:0,r=(r+1ll*p*ksm(q,mo-2))%mo; 17 } 18 return r; 19 } 20 int main() 21 { 22 scanf("%d%d%d",&a,&n,&mo),res=1,m=2*n+1; 23 for (int i=1;i<=n;i++) res=1ll*res*i%mo; 24 for (int i=0;i<=m;i++) f[0][i]=1; 25 for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) f[i][j]=(1ll*f[i-1][j-1]*j+f[i][j-1])%mo; 26 for (int i=1;i<=m;i++) y[i]=f[n][i]; 27 printf("%lld",1ll*res*change(a)%mo); 28 }