题目大意
%
给定
[
1
,
A
]
[1,A]
[1,A] 之间的数,从其中取出
m
m
m 个数排列,每种方案的价值为序列中所有元素的乘积,求所有方案的价值和在
m
o
d
mod
mod 意义下的值。
数据范围
1
⩽
A
⩽
1
0
9
,
n
⩽
500
,
m
o
d
∈
prime
,
m
o
d
⩽
1
0
9
,
m
o
d
>
A
>
n
+
1
1\leqslant A\leqslant 10^9,n\leqslant 500,mod\in \text{prime},mod\leqslant 10^9,mod>A>n+1
1⩽A⩽109,n⩽500,mod∈prime,mod⩽109,mod>A>n+1
题解
%
先考虑动态规划,直接问什么定义什么似乎很难转移。那就只能分析题目了。首先,我们发现方案的价值和顺序无关,因而我们可以求出严格上升序列的价值,然后乘上排列方案数
n
!
n!
n! 。
定义:用
1
∼
j
1\sim j
1∼j 填满序列的前
i
i
i 个位置,能得到的严格上升序列的总价值为
f
[
i
]
[
j
]
f[i][j]
f[i][j]。
那么转移就很好写了:
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
−
1
]
×
j
+
f
[
i
]
[
j
−
1
]
f[i][j]=f[i-1][j-1]\times j+f[i][j-1]
f[i][j]=f[i−1][j−1]×j+f[i][j−1] 则答案为
f
[
n
]
[
A
]
×
n
!
f[n][A]\times n!
f[n][A]×n!,时间复杂度为
Θ
(
n
×
A
)
\Theta(n\times A)
Θ(n×A),对于本题来说显然是错的。
考虑优化,如果我们知道
f
[
n
]
[
A
]
f[n][A]
f[n][A] 的次数,那就可以套拉格朗日插值了。
尝试令
f
[
i
]
[
j
]
f[i][j]
f[i][j] 是一个关于
i
i
i 的
g
(
n
)
g(n)
g(n) 次多项式。我们知道,一个
i
i
i 次多项式差分后为一个
i
−
1
i-1
i−1 次多项式,因而有:
f
[
i
]
[
j
]
−
f
[
i
−
1
]
[
j
]
f[i][j]-f[i-1][j]
f[i][j]−f[i−1][j] 为一个
g
(
i
)
−
1
g(i)-1
g(i)−1 次多项式。对方程式移项,得
f
[
i
]
[
j
]
−
f
[
i
]
[
j
−
1
]
=
f
[
i
−
1
]
[
j
−
1
]
×
j
f[i][j]-f[i][j-1]=f[i-1][j-1]\times j
f[i][j]−f[i][j−1]=f[i−1][j−1]×j 等式左侧为
g
(
i
)
−
1
g(i)-1
g(i)−1 次多项式,右侧为
g
(
n
−
1
)
+
1
g(n-1)+1
g(n−1)+1 次多项式,因而有:
g
(
n
)
−
1
=
g
(
n
−
1
)
+
1
g(n)-1=g(n-1)+1
g(n)−1=g(n−1)+1 化简,得:
g
(
n
)
=
g
(
n
−
1
)
+
2
g(n)=g(n-1)+2
g(n)=g(n−1)+2 可以用断言并用生成函数证明:
g
(
n
)
=
2
n
g(n)=2n
g(n)=2n 因而原方程为一个
2
n
2n
2n 次的多项式。我们先计算出
f
(
n
,
1
)
∼
f
[
n
]
[
2
n
+
1
]
f(n,1)\sim f[n][2n+1]
f(n,1)∼f[n][2n+1],时间复杂度为
Θ
(
n
)
\Theta(n)
Θ(n)。然后用拉格朗日插值求出
f
[
n
]
[
A
]
f[n][A]
f[n][A],总时间复杂度为
T
(
n
)
=
Θ
(
n
2
)
T(n)=\Theta(n^2)
T(n)=Θ(n2)
代码
#include<bits/stdc++.h>
using namespace std;
#define maxn 510
long long A,n;
long long mod;
long long f[maxn][maxn*2];
long long x[maxn],y[maxn];
long long pow_t(long long a,long long b){
if(b==1) return a%mod;
long long t=pow_t(a,b>>1);
if((b&1)==0) return t*t%mod;
return t*t%mod*a%mod;
}
#define inv(x) pow_t(x,mod-2)
long long ft(long long n,long long k){
long long ans=0;
for(int i=0;i<n;i++){
long long up=y[i]%mod,dn=1;
for(int j=0;j<n;j++)
if(i!=j)
up=up*(k-x[j]+mod)%mod,dn=dn*(x[i]-x[j]+mod)%mod;
ans=(ans+up*inv(dn)%mod)%mod;
} return ans;
}
int main(void)
{
scanf("%lld%lld%lld",&A,&n,&mod);
long long fac_n=1;
for(int i=1;i<=n;i++)
fac_n=fac_n*i%mod;
for(int i=0;i<=2*n+1;i++)
f[0][i]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=2*n+1;j++)
f[i][j]=(f[i-1][j-1]*j%mod+f[i][j-1])%mod;
for(int i=1;i<=2*n+1;i++)
x[i-1]=i,y[i-1]=f[n][i];
printf("%lld",fac_n*ft(2*n+1,A)%mod);
return 0;
}