1. 程式人生 > 實用技巧 >Solution -「AGC 012F」「AT 2366」Prefix Median

Solution -「AGC 012F」「AT 2366」Prefix Median

\(\mathcal{Description}\)

  Link.

  給定序列 \(\{a_{2n-1}\}\),將 \(\{a_{2n-1}\}\) 按任意順序排列後,令序列 \(b_i\) 為前 \(2i-1\) 個數的中位數。求 \(\{b_n\}\) 的個數,對 \(10^9+7\) 取模。

  \(n\le50\)

\(\mathcal{Solution}\)

  \(\{b_n\}\) 有一個很 naive 的性質:\(b_n\) 是常數,是 \(\{a_{2n-1}\}\) 的中位數。

  考慮擴充套件這一性質,從後往前,\(b_{i+1}\) 所對應的 \(a\) 序列刪去兩個數後就得到 \(b_i\)

所對應的序列。顯然,\(b_i\) 要麼是 \(b_{i+1}\) 在新序列的前驅或後記,要麼不變。

  形式地,有性質:

\[(\forall i\in[1,n))(\not\exists j\in[1,n])(b_i<b_j<b_{i+1}\lor b_{i+1}<b_j<b_i) \]

  接著,考慮到中位數本身的性質,將 \(\{a_{2n+1}\}\) 升序排列後,可以確定每個 \(b_i\) 的範圍:

\[a_i\le b_i\le a_{2n-i} \]

  那麼設狀態 \(f(i,j,k)\) 表示確定前 \(i\) 位,中位數左邊有 \(j\) 個數可用,右邊有 \(k\)

個數可用的方案數。轉移就……看程式碼吧 www~

\(\mathcal{Code}\)

#include <cstdio>
#include <algorithm>

const int MAXN = 50, MAXM = 100, MOD = 1e9 + 7;
int n, m, a[MAXM + 5], f[2][MAXM + 5][MAXM + 5];

inline void addeq ( int& a, const int b ) { if ( ( a += b ) >= MOD ) a -= MOD; }

int main () {
	scanf ( "%d", &n ), m = 2 * n;
	for ( int i = 1; i < m; ++ i ) scanf ( "%d", &a[i] );
	std::sort ( a + 1, a + m );
	f[0][0][0] = 1;
	for ( int i = n, t = 0; i > 1; -- i, t ^= 1 ) {
		bool dl = a[i] ^ a[i - 1], dr = a[m - i + 1] ^ a[m - i];
		for ( int j = 0; j < m; ++ j ) {
			for ( int k = 0; k < m; ++ k ) {
				int& cur = f[t][j][k];
				if ( ! cur ) continue;
				addeq ( f[t ^ 1][j + dl][k + dr], cur );
				for ( int p = 0; p < j + dl; ++ p ) addeq ( f[t ^ 1][p][k + dr + 1], cur );
				for ( int p = 0; p < k + dr; ++ p ) addeq ( f[t ^ 1][j + dl + 1][p], cur );
				cur = 0;
			}
		}
	}
	int ans = 0;
	for ( int i = 0; i < m; ++ i ) {
		for ( int j = 0; j < m; ++ j ) {
			addeq ( ans, f[n & 1 ^ 1][i][j] );
		}
	}
	printf ( "%d\n", ans );
	return 0;
}