Official

F - Make Pair Editorial by en_translator


This problem can be solved with Segment DP (Dynamic Programming).

Segment DP is a DP where the indices denote a segment, which is useful for such problem that it is difficult to do DP over indices fixed only at one end, and that at least square time complexity is allowed.

In this problem, we can define DP as

\[ dp[i][j]=(\text{Number of ways to choose }j{ pairs from Student }i+1, \text{ Student }i+2 , \ldots \text{ and Student }i+2j). \]

The answer is \(dp[0][N]\). Alternatively, we can define the indices to be the indices of the leftmost and rightmost students.

Now, we will determine \(dp[i][j]\) in the increasing order of \(j\). There are several approaches to update them. Be careful not to count twice or overlook something. For example, we can divide into cases depending on which student make a pair with Student \(i+1\). Note that Student \(i+1\) can only paired with Student \(i+2k\) due to the parity.
Now let us consider how many ways there are to choose \(j\) pairs from Student \(i+1\), Student \(i+2\), \(\ldots\), and Students \(i+2j\), assuming that Student \(i+1\) is paired with Student \(i+2k\).

Firstly, the first \(2k\) students and the last \(2(j-k)\) are paired with students in that group, respectively. The number of ways to choose pairs from Students \(i+1\), \(\ldots\), \(i+2k\) is \(0\) if Student \(i+1\) and Student \(i+2k\) are on bad terms; if they are on good terms, the only ways to choose pairs are to choose \(k-1\) pairs from Students \(i+2\), \(\ldots\), \(i+2k-1\) first, and then to make a pair of Student \(i+1\) and Student \(i+2k\), so it is equal to the number of the ways to make \(k-1\) pairs, that is, \(dp[i+1][k-1]\). Additionally, there are \(dp[i+2k][j-k]\) ways to choose \(j-k\) pairs from Students \(i+2k+1\), \(\ldots\), \(i+2j\), and finally we can choose the arbitrary order of from which group we choose pairs, group of Students \(i+1\), \(\ldots\) , \(i+2k\) or group of Students \(i+2k+1\), \(\ldots\), \(i+2j\), so we can find that

\[ dp[i][j]=\sum_{k=1}^j (dp[i+1][k-1]\times dp[i+2k][j-k]\times {}_jC_k \times \mathrm{friends}[i+1][i+2k]). \]

Here, \(\mathrm{friends}[i][j]\) is \(1\) if Student \(i\) and Student \(j\) are on good terms, or \(0\) if they are on bad terms. With the initialization \(dp[0][0]=dp[1][0]=\cdots =dp[2n][0]=1\), we can update them with the relations above to find the solution.

Here, each update can be performed in a time complexity of \(O(N)\), so the total complexity is \(O(N^3)\), which is fast enough. Therefore, the problem has been solved.

Sample code in C++:

#include <bits/stdc++.h>

using namespace std;

#define N 410
#define ll long long
#define MOD 998244353
#define rep(i, n) for(int i = 0; i < n; ++i)

int main(void) {
	int n, m;
	int a, b;
	ll x, y, z;
	bool can[N][N];
	ll dp[N][(N / 2)];
    ll binom[N][N];
	binom[0][0] = 1;
	for (int i = 1; i < N; i++) {
		binom[i][0] = 1;
		binom[i][i] = 1;
		rep(j, i - 1) {
			binom[i][j + 1] = (binom[i - 1][j] + binom[i - 1][j + 1]) % MOD;
		}
	}

	cin >> n >> m;
	rep(i, 2 * n)rep(j, 2 * n)can[i][j] = false;
	rep(i, m) {
		cin >> a >> b;
		can[a - 1][b - 1] = true;
		can[b - 1][a - 1] = true;
	}
	rep(i, 2 * n + 1)dp[i][0] = 1;
	for (int j = 1; j <= n; j++) {
		rep(i, 2 * (n - j) + 1) {
			dp[i][j] = 0;
			rep(k, j) {
				if (can[i][i + (2 * k) + 1]) {
					x = (dp[i + 1][k] * dp[i + (2 * k) + 2][j - k - 1]) % MOD;
					dp[i][j] = ((x*binom[j][k + 1]) + dp[i][j]) % MOD;
				}
			}
		}
	}
	cout << dp[0][n] << endl;

	return 0;
}

posted:
last update: