「2017 山東一輪集訓 Day4」塔 - dp - 矩陣乘法
阿新 • • 發佈:2018-12-09
題目大意:
現在有一條
的數軸,要在上面造
座塔,每座塔的座標要兩兩不同,且為整點。塔有編號,且每座塔都有高度,對於編號為
座塔,其高度為
。對於一座塔,需要滿足它與前面以及後面的塔的距離大於等於自身高度(不存在則沒有限制)。問有多少建造方案。答案對
取模。塔不要求按編號為順序建造。
題解:
考慮給你一個排列怎麼算有多少种放法,顯然令
,則相當於,若記
為
到
的距離,則
,特殊的
,並且
,可知方案數是
。
因此dp所有排列的S的資訊,令dp(i,j,k)表示,從小到大考慮了前i個數字,當前的S是j,並且形成了k段的方案數,轉移有三種:單獨稱為一段,放在某一段的一端,或者合併兩端。
由於m不是質數,因此要注意組合數怎麼算:注意到C(L-1-S+n,n)的第一維變化範圍是
的,因此先用矩乘跑出
,然後遞推出剩下的即可。
複雜度
#include<bits/stdc++.h>
#define gc getchar()
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define ull unsigned lint
#define db double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn()
{
int x,ch;while((ch=gc)<'0'||ch>'9');
x=ch^'0';while((ch=gc)>='0'&&ch<='9')
x=(x<<1)+(x<<3)+(ch^'0');return x;
}
const int N=110,MXS=10010;
int dp[2][MXS][N],mod;
inline int upd(int &x,int y) { return x+=y,(x>=mod?x-=mod:0); }
namespace Binom_Space{
int u,d,c[MXS][N];
struct matrix{
int v[N][N],n;matrix(int _n=0) { init(_n); }
inline int init(int _n) { n=_n;rep(i,0,n) memset(v[i],0,sizeof(int)*(n+1));return 0; }
inline matrix operator*(const matrix &b)const
{
const matrix &a=*this;matrix c(n);
rep(i,0,n) rep(k,0,n) rep(j,0,n) upd(c.v[i][j],(lint)a.v[i][k]*b.v[k][j]%mod);
return c;
}
inline matrix& operator*=(const matrix &b) { return (*this)=(*this)*b; }
}A;
inline matrix fast_pow(matrix A,int k)
{
matrix ans(A.n);rep(i,0,A.n) ans.v[i][i]=1;
for(;k;k>>=1,A*=A) if(k&1) ans*=A;return ans;
}
inline int getC(int _u,int _d,int n)
{
u=_u,d=_d,A.init(n),A.v[0][0]=1;
rep(i,1,n) A.v[i][i-1]=A.v[i][i]=1;
A=fast_pow(A,u);
rep(i,0,n) c[0][i]=A.v[i][0];
rep(i,1,d-u) c[i][0]=1;
rep(i,1,d-u) rep(j,1,n) c[i][j]=c[i-1][j-1]+c[i-1][j],(c[i][j]>=mod?c[i][j]-=mod:0);
return 0;
}
inline int C(int n,int m) { return c[n-u][m]; }
}using Binom_Space::C;
int main()
{
int n=inn(),L=inn();mod=inn();
int now=1,nxt=0,lwr=0,upr=0;dp[now][0][1]=1;
for(int i=1;i<n;i++,swap(nxt,now))
{
rep(j,0,i*(i+1)) memset(dp[nxt][j],0,sizeof(int)*(i+1+1));
int nxtlwr=i*(i+1),nxtupr=0;
rep(j,lwr,upr) rep(k,1,i) if(dp[now][j][k])
upd(dp[nxt][j][k+1],dp[now][j][k]*(k+1ll)%mod),
upd(dp[nxt][j+i+1][k],dp[now][j][k]*2ll*k%mod),
upd(dp[nxt][j+2*(i+1)][k-1],dp[now][j][k]*(k-1ll)%mod),
nxtlwr=min(nxtlwr,j),nxtupr=max(nxtupr,j+2*(i+1));
lwr=nxtlwr,upr=nxtupr;
}
int u=max(L-1-upr+n,0),d=max(0,L-1-lwr+n),ans=0;
Binom_Space::getC(u,d,n);
rep(s,lwr,upr) if(dp[now][s][1])
upd(ans,(lint)C(L-1-s+n,n)*dp[now][s][1]%mod);
return !printf("%d\n",ans);
}