kmjp's blog

競技プログラミング参加記です

yukicoder : No.336 門松列列

これは★3でもいいかも。実装はNo.335より簡単だし…。
http://yukicoder.me/problems/933

問題

正整数Nが与えられる。
1~Nのpermutationのうち、どの連続する3要素を取っても門松列を成しているようなもの(門松列列)はいくつあるか。

解法

k要素の門松列列のうち、1値目より2値目が小さいものの数をF(k)とする。
1値目より2値目が小さいものと大きいものは同数あると考えると、求めたい値はF(N)*2である。

1~kのpermutation(門松列列とは限らない)のどこかに(k+1)を挿入し、1値目より2値目が小さい(k+1)要素の門松列列を作ることを考える。
(k+1)をx+1番目の要素となる位置に挿入することを考える。
先頭x個が偶数個かつ1値目より2値目が小さい門松列列で、末尾(k-x)個が1値目より2値目が大きい門松列列であれば、それらを連結してできる
[(x要素(偶数)からなる1値目より2値目が小さい門松列列),(k+1),((k-x)要素からなる1値目より2値目が大きい門松列列)]という形の数列は(k+1)要素の門松列列をなす。

よって偶数xに対し、F(k+1) += F(x)*F(k-x)*Comb(k,x)で足しこんでいけばF(k+1)を求められる。
(Combinationは、k要素中どのx個を(k+1)の手前に持ってくるか選ぶ選び方の分)

この問題の解としてはN=1,2の際は0と答えるが、以下の計算ではF(0)=F(1)=F(2)=1とおく点に注意。

ll N;
ll mo=1000000007;
const int CN=4001;
ll C[CN][CN];
ll dp[2300];

void solve() {
	int i,j,k,l,r,x,y; string s;
	
	FOR(i,CN) for(j=0;j<=i;j++) C[i][j]=(j==0||j==i)?1:(C[i-1][j-1]+C[i-1][j])%mo;
	
	cin>>N;
	if(N<=2) return _P("0\n");
	
	dp[0]=dp[1]=dp[2]=1;
	dp[3]=2;
	for(i=3;i<=N-1;i++) for(x=0;x<=i;x+=2) (dp[i+1]+=C[i][x]*dp[x]%mo*dp[i-x])%=mo;
	
	cout<<dp[N]*2%mo<<endl;
	
}

まとめ

実装は単純。