SRM592 Div1 Medium - LittleElephantAndPermutationDiv1

類題(前提知識)
creep06.hatenablog.com

問題

長さnの順列a,bがあるとき、
magic(a,b) = max(a[1],b[1]) + max(a[2],b[2]) + ... + max(a[n],b[n])
と定義する
magic(a,b)の値がk以上になるようなa,bの組み合わせの数を求めよ

1 <= n <= 50
1 <= k <= 2500

解法

How to deal with two permutations? Matching DP - YouTube
この動画の解説がすごくわかりやすかった

片方の順列は{1,2,...,n}と固定しても問題はない
(それで数え上げた結果を最後にn!倍すれば良い)
すると↑の類題と同じような考え方ができるようになる
dp[i][j][L]: i番目まで見て、両方の順列それぞれでまだペアになっていない数字がj個あり、今までのmagic()の和がLであるような場合の数
各iでペアを確定するということはiがそのペアのmaxになるから容易に計算することができる
遷移はコードに書いてある通り

実装

dp配列は節約しないとスタックオーバーフローするから注意

#define reps(i,s,n) for(long long (i)=(s);(i)<(n);++(i))
#define rep(i,n) reps(i,0,n)

class LittleElephantAndPermutationDiv1 {
public:
	int getNumber(int n, int k) {
		ll dp[2][55][3030] = {};
		dp[0][0][0] = 1;
		rep(I,n) {
			int i = I&1;
			rep(j,n) rep(l,2525) {
				// l[i]とr[i]両方のペアをjから決める
				if (j) (dp[i^1][j-1][l+(I+1)*2] += dp[i][j][l]*j %mod *j) %= mod;
				// l[i]かr[i]どっちかのペアだけjから決める
				(dp[i^1][j][l+I+1] += dp[i][j][l]*j*2) %= mod;
				// l[i]とr[i]をペアにする
				(dp[i^1][j][l+I+1] += dp[i][j][l]) %= mod;
				// 何も決めない
				(dp[i^1][j+1][l] += dp[i][j][l]) %= mod;
			}
			memset(dp[i],0,sizeof(dp[i]));
		}
		ll res = 0;
		reps(i,k,2525) (res += dp[n&1][0][i]) %= mod;
		// 並べ方がn!通りある
		rep(i,n) (res *= i+1) %= mod;
		return res;
	}
};