Live2D

Solution -「LOCAL」客星璀璨之夜

\(\mathcal{Description}\)

  OurOJ.

  给定坐标轴上的 \(2n+1\) 个坐标 \(x_1,x_2,\cdots,x_{2n+1}\),其中偶数下标的位置是一个小球,奇数下标的位置是一个球洞。每次操作随机选择一个小球,并随机让它向左或向右滚入临近的球洞,该球洞被填满,视作平地。求所有球进洞后,球滚动总距离的期望。对 \(998244353\) 取模。

  \(n\le3000\)

\(\mathcal{Solution}\)

  显然,\(n\) 个球进洞的总方案为 \(2^nn!\),记为 \(g(n)\)。现只需要计算所有方案的滚动距离之和。坐标实际位置并不重要,考虑一段形如 \(x_i\leftrightarrow x_{i+1}\) 的距离在多少种方案中贡献。

  贡献次数显然仅与 \(n\) 和位置 \(i\) 有关。令 \(f(i,j)\) 表示仅有 \(i\) 个球(和 \(i+1\) 个洞)时,\(x_j\leftrightarrow x_{j+1}\) 的贡献次数。转移时,考虑当前局面第一次操作:

  • \(x_j\)\(x_{j+1}\) 配对消失,贡献次数为剩下 \(i-1\) 个球撞完的总方案数,即 \(g(n-1)\),并且 \(x_j\leftrightarrow x_{j+1}\) 这一段被纳入了\(x_{j-1}\leftrightarrow x_{j+2}\),转移需要让坐标前移一位,那么 \(f(i,j) \leftarrow f(i,j)+g(i-1)+f(i-1,j-1)\)

  • 操作 \(j\) 前面的球,有 \(j-1\) 种等价操作方式,当前这段没有贡献,坐标向前两位,即 \(f(i,j) \leftarrow f(i,j)+(j-1)f(i-1,j-2)\)

  • 操作 \(j+1\) 后面的球,有 \(2i-j\) 种等价操作方式,当前这段还是没有贡献,坐标也没有影响,即 \(f(i,j) \leftarrow f(i,j)+(2i-j)f(i-1,j)\)

  综上:

\[ f(i,j)=g(i-1)+f(i-1,j-1)+(j-1)f(i-1,j-2)+(2i-j)f(i-1,j) \]

  答案显而易见:

\[ \frac{\sum_{i=1}^{2n}(x_{i+1}-x_i)f(n,i)}{g(n)} \]

  复杂度 \(\mathcal O(n^2)\)

\(\mathcal{Code}\)

/* Clearink */

#include <cstdio>

inline int rint () {
	int x = 0, f = 1; char s = getchar ();
	for ( ; s < '0' || '9' < s; s = getchar () ) f = s == '-' ? -f : f;
	for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' );
	return x * f;
}

template<typename Tp>
inline void wint ( Tp x ) {
	if ( x < 0 ) putchar ( '-' ), x = ~ x + 1;
	if ( 9 < x ) wint ( x / 10 );
	putchar ( x % 10 ^ '0' );
}

const int MAXN = 3000, MOD = 998244353;
int n, m, x[MAXN * 2 + 5], g[MAXN + 5], f[MAXN + 5][MAXN * 2 + 5];

inline int& addeq ( int& a, const int b ) {
	return ( a += b ) < MOD ? a : a -= MOD;
}

inline int qkpow ( int a, int b, const int p = MOD ) {
	int ret = 1;
	for ( ; b; a = 1ll * a * a % p, b >>= 1 ) ret = 1ll * ret * ( b & 1 ? a : 1 ) % p;
	return ret;
}

int main () {
//	freopen ( "stars.in", "r", stdin );
//	freopen ( "stars.out", "w", stdout );
	m = ( n = rint () ) << 1 | 1;
	for ( int i = 1; i <= m; ++ i ) x[i] = rint ();
	g[0] = 1;
	for ( int i = 1; i <= n; ++ i ) {
		g[i] = 2ll * i * g[i - 1] % MOD;
		for ( int j = 1; j <= i << 1; ++ j ) {
			addeq ( addeq ( addeq ( addeq ( f[i][j], g[i - 1] ), f[i - 1][j - 1] ),
				( j - 1ll ) * f[i - 1][j - 2 < 0 ? 0 : j - 2] % MOD ),
				( 2ll * i - j ) * f[i - 1][j] % MOD
			);
		}
	}
	int ans = 0;
	for ( int i = 1; i <= n << 1; ++ i ) {
		addeq ( ans, 1ll * ( x[i + 1] - x[i] ) * f[n][i] % MOD );
	}
	wint ( 1ll * ans * qkpow ( g[n], MOD - 2 ) % MOD ), putchar ( '\n' );
	return 0;
}

\(\mathcal{Details}\)

  考场上想的统计每一对 \(i\) 球撞 \(j\) 洞的出现次数,但这个涉及到多类方案的交叉安排,而且方案间有依赖关系……就死掉啦。

  还有,暴力打半天过不了样例,手玩了一下发现距离贡献没乘方案数 qwq。

posted @ 2020-10-07 09:37  Rainybunny  阅读(401)  评论(0)    收藏  举报