1. 程式人生 > >「2017 山東一輪集訓 Day4」塔 - dp - 矩陣乘法

「2017 山東一輪集訓 Day4」塔 - dp - 矩陣乘法

題目大意:
現在有一條 [ 1 , l ] [1, l] 的數軸,要在上面造 n

n 座塔,每座塔的座標要兩兩不同,且為整點。塔有編號,且每座塔都有高度,對於編號為 i i 座塔,其高度為 i i 。對於一座塔,需要滿足它與前面以及後面的塔的距離大於等於自身高度(不存在則沒有限制)。問有多少建造方案。答案對 m
m
取模。塔不要求按編號為順序建造。
題解:
考慮給你一個排列怎麼算有多少种放法,顯然令 S = i = 1
n 1 max ( p i , p i + 1 ) S=\sum_{i=1}^{n-1}\max(p_i,p_{i+1})
,則相當於,若記 x i x_i p i p_i p i + 1 p_{i+1} 的距離,則 x i max ( p i , p i + 1 ) x_i\ge\max(p_i,p_{i+1}) ,特殊的 min ( x 0 , x n ) 0 \min(x_0,x_n)\ge0 ,並且 i = 0 n x i = L 1 \sum_{i=0}^nx_i=L-1 ,可知方案數是 ( L 1 S + n n ) \binom{L-1-S+n}{n}
因此dp所有排列的S的資訊,令dp(i,j,k)表示,從小到大考慮了前i個數字,當前的S是j,並且形成了k段的方案數,轉移有三種:單獨稱為一段,放在某一段的一端,或者合併兩端。
由於m不是質數,因此要注意組合數怎麼算:注意到C(L-1-S+n,n)的第一維變化範圍是 O ( n 2 ) O(n^2) 的,因此先用矩乘跑出 C ( L 1 max S + n , 0 n ) C(L-1-\max S+n,0\dots n) ,然後遞推出剩下的即可。
複雜度 O ( n 4 + n 3 l g L ) O(n^4+n^3lgL)

#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);
}