1. 程式人生 > 其它 >第20屆上海大學程式設計聯賽春季賽 F - 到底是多少分啊

第20屆上海大學程式設計聯賽春季賽 F - 到底是多少分啊

連結:https://ac.nowcoder.com/acm/contest/33785/F
來源:牛客網
在旅途的終點,你終於看到了大魔王小 X。
小 X 準備玩一個遊戲,這個遊戲規則如下:
對於一個含有 n 個數的陣列 a。小 X 必須執行如下操作 t 次:
選擇陣列中的某一個數,給它加上 1。
經過這樣 t 次操作後得到的陣列 b。小 X 的得分是陣列 b 中所有數的乘積。
顯然,在不同的操作過程後,最終可能獲得不同的陣列 b。我們稱兩個陣列是不同的,當且僅當這兩個陣列中至少有一個數不同,即兩個陣列 p, q 不同當且僅當存在 i 使得 pi≠qi 。
如果小 X 在所有不同的陣列 b 中隨機取一個作為最終結果,那麼他的期望得分是多少?

  • 題目要求的是期望得分,等於對\(n\)個數進行\(t\)次操作,能帶來的得分總和除以操作的種類數。操作的種類我們可以看做球盒模型(\(t\)相同的球放進\(n\)個不同的盒子,可空)答案為\(C_{n + t - 1}^{n - 1}\),於是只需要求得得分總和即可。
  • 考慮令\(f_{i,j}\)表示對前\(i\)個數進行\(j\)次操作能獲得的不同的得分之和,狀態轉移的時候我們列舉在第\(i\)個數進行次\(k\)次操作,即
\[f_{i,j} = \sum_{k = 0}^{j - 1}(f_{i-1,j - k}\times (a_i + k)) \]

很明顯這個轉移時樸素的\(n^3\)

,對於這個列舉當前操作幾次的\(dp\),我們考慮把它的形式拆開並且這樣寫觀察一下

\[f_{i,j} = f_{i-1,j}\times a_i +f_{i-1,j - 1}\times (a_i + 1) + f_{i-1,j - 2}\times (a_i + 2) + \cdots +f_{i-1,0}\times (a_i + j) \\ f_{i,j - 1} = f_{i-1,j - 1}\times a_i + f_{i-1,j-2}\times (a_i + 1)\cdots +f_{i-1,0}\times (a_i + j - 1) \]

可得

\[f_{i,j} = f_{i-1,j}\times a_i + f_{i,j - 1} + \sum_{k = 0}^{j - 1}f_{i-1,k} \]

這樣維護一個字首和就可以\(O(n^2)\)

轉移了。
注意一下初始化的問題,僅讓\(f_{0,0} = 1\)即可,他來更新最開始的答案,其它的直接轉移即可。

#include <bits/stdc++.h>

using namespace std;

const long long mod = 998244353;

long long f[3010][3010],a[3010],s[3010][3010],fac[10010];
long long fiv[10010],inv[10010];

void init() {
	fac[0] = fac[1] = fiv[0] = fiv[1] = inv[1] = 1;
	for(int i = 2;i <= 10000;i ++) {
		fac[i] = fac[i - 1] * i % mod;
		inv[i] = (mod - mod / i) * inv[mod % i] % mod;
		fiv[i] = inv[i] * fiv[i - 1] % mod;
	}
}

long long ksm(long long a,long long b) {
	long long res = 1;
	while(b) {
		if(b & 1) res = res * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}

long long invv(int x) {
	return ksm(1ll * x,mod - 2);
}

long long C(int n,int m) {
	return fac[n] * fiv[m] % mod * fiv[n - m] % mod;
}

int main() {
	init();
	// fac[0] = 1;
	// for(int i = 1;i <= 10000;i ++) fac[i] = fac[i - 1] * i % mod;
	int n,t; cin >> n >> t;
	for(int i = 1;i <= n;i ++) {
		cin >> a[i];
	}
	s[0][0] = f[0][0] = 1;
	for(int i = 1;i <= n;i ++) {
		f[i][0] = f[i - 1][0] * a[i] % mod;  
		s[i][0] = f[i][0];
	}
	for(int i = 1;i <= t;i ++) {
		s[0][i] = (s[0][i - 1] + f[0][i]) % mod;
	}
	for(int i = 1;i <= n;i ++) {
		for(int j = 1;j <= t;j ++) {
			f[i][j] = f[i - 1][j] * a[i] % mod;
			f[i][j] = (f[i][j] + f[i][j - 1]) % mod;
			f[i][j] = (f[i][j] + s[i - 1][j - 1]) % mod;
		}
		for(int j = 1;j <= t;j ++) {
			s[i][j] = (s[i][j - 1] + f[i][j]) % mod;
		}
	}
	long long ans = f[n][t] * invv(C(n + t - 1,n - 1)) % mod;
	cout << ans << '\n';
	return 0;
}