題目大意
有一條
問有多少種建造方案。答案對
題目分析
先考慮這樣一個暴力:枚舉塔的排列順序,然後我們可以計算出按順序安放這些塔至少需要的空間,假設其爲
然後我們剩下
可以發現,塔的排列順序固然有很多種,但是這些排列計算出來的
考慮從大到小放塔。考慮放置塔
令
當然還要注意一下邊界情況,我的處理方法是在兩邊各加一個高塔,最後所有塔合併在一起。
直接做貌似會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;
}