显然可以dp
显然可以假设单调增,答案乘个阶乘即可
设 f ( i , j ) f(i,j) f(i,j)表示前 i i i个不超过 j j j的答案
f ( i , j ) = f ( i , j − 1 ) + j f ( i − 1 , j − 1 ) f(i,j)=f(i,j-1)+jf(i-1,j-1) f(i,j)=f(i,j−1)+jf(i−1,j−1)
注意边界是 f ( 0 , i ) = 1 ! f(0,i)=1! f(0,i)=1!
注意边界是 f ( 0 , i ) = 1 ! ! f(0,i)=1!! f(0,i)=1!!
注意边界是 f ( 0 , i ) = 1 ! ! ! f(0,i)=1!!! f(0,i)=1!!!
最终答案为 n ! f ( n , A ) n!f(n,A) n!f(n,A)
这样做是 O ( n A ) O(nA) O(nA)的,需要优化
考虑上网搜题解,这样容易得知可以假设 f ( i , j ) f(i,j) f(i,j)是关于 j j j的多项式
人话翻译:把 f f f每一行拆开看成一个个数列,这样每一行有一个多项式通项公式
设 g ( i ) g(i) g(i)表示 f ( i , j ) f(i,j) f(i,j)的次数
根据转移方程
f ( i , j ) − f ( i , j − 1 ) = j f ( i − 1 , j − 1 ) f(i,j)-f(i,j-1)=jf(i-1,j-1) f(i,j)−f(i,j−1)=jf(i−1,j−1)
看着不习惯?
F ( i ) − F ( i − 1 ) = i f ( i − 1 ) F(i)-F(i-1)=if(i-1) F(i)−F(i−1)=if(i−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 ( 0 ) = 0 g(0)=0 g(0)=0
所以
g ( n ) = 2 n g(n)=2n g(n)=2n
插一下就好了
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
const int N=1005;
using namespace std;
int MOD;
typedef long long ll;
inline int add(const int& a,const int& b){return a+b>=MOD? a+b-MOD:a+b;}
inline int dec(const int& a,const int& b){return a<b? a-b+MOD:a-b;}
inline int qpow(int a,int p)
{
int ans=1;
while (p)
{
if (p&1) ans=(ll)ans*a%MOD;
a=(ll)a*a%MOD;p>>=1;
}
return ans;
}
int dp[N][N];
int main()
{
int a,n;
scanf("%d%d%d",&a,&n,&MOD);
for (int i=0;i<=(2*n);i++) dp[0][i]=1;
for (int i=1;i<=n;i++)
for (int j=1;j<=(2*n);j++)
dp[i][j]=add(dp[i][j-1],(ll)j*dp[i-1][j-1]%MOD);
int ans=0;
for (int i=0;i<=2*n;i++)
{
int mul=dp[n][i];
if (!mul) continue;
for (int j=0;j<=2*n;j++) if (i!=j) mul=(ll)mul*dec(a,j)%MOD*qpow(dec(i,j),MOD-2)%MOD;
ans=add(ans,mul);
}
for (int i=1;i<=n;i++) ans=(ll)ans*i%MOD;
printf("%d\n",ans);
return 0;
}
本文介绍了一种使用动态规划(DP)解决特定传送门问题的方法。通过构建递推关系,文章详细阐述了如何求解在有限制条件下通过传送门到达目标的方案数,并通过代码示例展示了具体的实现过程。


被折叠的 条评论
为什么被折叠?



