传送门
显然可以dp
显然可以假设单调增,答案乘个阶乘即可
设f(i,j)f(i,j)f(i,j)表示前iii个不超过jjj的答案
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,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(nA)O(nA)O(nA)的,需要优化
考虑上网搜题解,这样容易得知可以假设f(i,j)f(i,j)f(i,j)是关于jjj的多项式
人话翻译:把fff每一行拆开看成一个个数列,这样每一行有一个多项式通项公式
设g(i)g(i)g(i)表示f(i,j)f(i,j)f(i,j)的次数
根据转移方程
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,j)−f(i,j−1)=jf(i−1,j−1)
看着不习惯?
F(i)−F(i−1)=if(i−1)F(i)-F(i-1)=if(i-1)F(i)−F(i−1)=if(i−1)
由于左边是多项式,最高项一定消掉了
所以
g(n)−1=g(n−1)+1g(n)-1=g(n-1)+1g(n)−1=g(n−1)+1
g(n)=g(n−1)+2g(n)=g(n-1)+2g(n)=g(n−1)+2
显然
g(0)=0g(0)=0g(0)=0
所以
g(n)=2ng(n)=2ng(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;
}