TopCoder SRM592 Div1 Med 500 LittleElephantAndPermutationDiv1
問題文概要
$ (1, \dots, n) $ の($ 1 $-origin の)順列のペア $ \sigma, \pi $ に対し, $$ f(\sigma, \pi) = \sum_{i = 1}^{n} \max(\sigma(i), \pi(i)) $$ とする.
$ f(\sigma, \pi) \ge K $ となる順列のペアの個数を答えよ.
制約
$ 1 \le n \le 50,\ 1 \le K \le 2500 $.
解法
解らなくて editorial 見た.
$ n $ から順に, 同時に $ \sigma, \pi $ 内の場所を決めていく.
既に相方に置いてある場所は, ($ n $ から順に置いているので), $ f $ は何も得られない.
最初は
n | |||
---|---|---|---|
n |
($ 2n $ 得られる)とか
n | |||
---|---|---|---|
n |
($ n $ 得られる)みたいなのがある.
その次は,
? | n-1 | ||
---|---|---|---|
? | n-1 |
とか, いくつか置き方がある.
とにもかくにも, DP を ($i$ を置く, ペアで埋まっている個数, 残り必要な f の値) ですればいい.
ソースコード
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 189.75 pts
constexpr int mod = 1E9 + 7;
// i を入れる.
// 両方空いている + 片方ずつ空いている = i.
// 両方空いてるのが c マス.
array<array<array<int, 2600>, 60>, 60> dp;
array<int, 60> factorial_double;
int dfs(int i, long long c, int need){
if(need <= 0) return factorial_double[i+1];
if(i == 0) return need <= 0;
if(dp[i][c][need] != -1) return dp[i][c][need];
const long long x = i+1 - c;
long long res = 0;
// 両方 "片方空いている" に入れる
if(x) (res += x * x * dfs(i-1, c, need)) %= mod;
// 一方だけ "片方空いている" に入れる
if(x and c) (res += x * c * 2 * dfs(i-1, c-1, need - i)) %= mod;
// 両方 "両方空いている" に入れる
if(c) (res += c * dfs(i-1, c-1, need - i)) %= mod;
if(c >= 2) (res += c * (c - 1) * dfs(i-1, c-2, need - i - i)) %= mod;
return dp[i][c][need] = res;
}
int LittleElephantAndPermutationDiv1::getNumber( int n, int K ){
K -= n;
factorial_double[0] = 1;
rep(i, 55) factorial_double[i+1] = (long long) factorial_double[i] * (i+1) * (i+1) % mod;
rep(i, 60) rep(j, 60) rep(k, 2600) dp[i][j][k] = -1;
return dfs(n-1, n, K);
}