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

猜你喜欢

转载自blog.csdn.net/Mys_C_K/article/details/84896134