题目大意
有一条
问有多少种建造方案。答案对
题目分析
先考虑这样一个暴力:枚举塔的排列顺序,然后我们可以计算出按顺序安放这些塔至少需要的空间,假设其为
然后我们剩下
可以发现,塔的排列顺序固然有很多种,但是这些排列计算出来的
考虑从大到小放塔。考虑放置塔
令
当然还要注意一下边界情况,我的处理方法是在两边各加一个高塔,最后所有塔合并在一起。
直接做貌似会MLE+TLE。首先我们要滚动第一维,其次后面两维我们都可以算出一个并不需要太紧的上下界来枚举,加上这两个优化就可以通过了。
至于组合数,你可以矩阵乘法加递推,也可以分解质因数算一算。这个自己脑补一下就好了。
时间复杂度
代码实现
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cassert>
#include <cstdio>
using namespace std;
const int N=105;
int pri[N],cnt[N],tmp[N],mip[N],pid[N];
int f[2][N*N][N];
bool mark[N];
int n,m,l,ans;
void pre()
{
mark[1]=1,mip[1]=1;
for (int i=2;i<=n;++i)
{
if (!mark[i]) pri[++pri[0]]=mip[i]=i,pid[i]=pri[0];
for (int j=1;j<=pri[0];++j)
{
if (1ll*pri[j]*i>n) break;
mark[pri[j]*i]=1,mip[pri[j]*i]=pri[j];
if (!(i%pri[j])) break;
}
}
for (int i=1;i<=n;++i)
for (int x=i;x!=1;)
for (int y=mip[x];!(x%y);x/=y,++cnt[pid[y]]);
}
bool dp()
{
f[0][0][1]=1;
bool x=1,y=0;
for (int i=n+1,upper=0,lower=0;i>1;lower+=i<=n?i:0,--i,upper+=i<<1)
{
x^=1,y^=1,memset(f[y],0,sizeof f[y]);
for (int j=lower;j<=upper&&j<=n*n;++j)
for (int k=1;k<=i+2&&k<=n-i+3;++k)
{
if (!f[x][j][k]) continue;
(f[y][j][k-1]+=1ll*f[x][j][k]*k%m)%=m;
(f[y][j+i-1][k]+=1ll*f[x][j][k]*(k*2)%m)%=m;
(f[y][j+(i-1<<1)][k+1]+=1ll*f[x][j][k]*k%m)%=m;
}
}
return y;
}
int mult(int st,int en)
{
int ret=1;
for (int i=1;i<=pri[0];++i) tmp[i]=cnt[i];
for (int i=st;i<=en;++i)
{
int x=i;
for (int j=1;j<=pri[0];++j)
for (;tmp[j]&&!(x%pri[j]);x/=pri[j],--tmp[j]);
ret=1ll*ret*x%m;
}
int tot=0;
for (int j=1;j<=pri[0];++j) tot+=!tmp[j];
assert(tot==pri[0]);
return ret;
}
void calc()
{
pre(),ans=0;
for (int i=0,x=dp();i<=n*n&&i<=l;++i)
if (f[x][i][0]) (ans+=1ll*f[x][i][0]*mult(l-i,l-i+n-1)%m)%=m;
}
int main()
{
freopen("tower.in","r",stdin),freopen("tower.out","w",stdout);
scanf("%d%d%d",&n,&l,&m),calc(),printf("%d\n",ans);
fclose(stdin),fclose(stdout);
return 0;
}