1. 程式人生 > 其它 >【YBT2022寒假Day6 A】【luogu CF891E】隨機減法 / Lust(EGF)

【YBT2022寒假Day6 A】【luogu CF891E】隨機減法 / Lust(EGF)

給你一個數組,每次隨機選一個數減一,然後貢獻增加除了這個數以外所有數的乘積,然後問你操作 k 次之後期望的貢獻和。

隨機減法 / Lust

題目連結:YBT2022寒假Day6 A / luogu CF891E

題目大意

給你一個數組,每次隨機選一個數減一,然後貢獻增加除了這個數以外所有數的乘積,然後問你操作 k 次之後期望的貢獻和。

思路

它這個除了以外某個數以外的乘積很不好搞,我們考慮一定把它弄成跟全部乘積有關的。
然後你發現每次減一,那全部乘積就減少了除了這個數以外所有數的乘積。

那每次的貢獻就是全部乘積的減小量,那總貢獻就是全部乘積總共減少的量,也就是: \(\prod a_i-\prod(a_i-b_i)\)\(b_i\)\(k\) 次操作選了 \(a_i\) 多少次)
然後 \(b_i\) 是每個的次數,你還要放的順序,所以要乘上 \(\dfrac{k!}{\prod b_i!}\)

然後因為是期望,所以要乘概率:\(\dfrac{1}{n^k}\)
所以我們要得到的就是:\(\prod a_i-\sum\limits_{b}\dfrac{1}{n^k}\dfrac{k!}{\prod b_i!}\prod(a_i-b_i)\)

然後把 \(b\) 的放在一起:\(\dfrac{k!}{n^k}\prod\dfrac{a_i-b_i}{b_i!}\)
你發現右邊這個 \(\prod\) 裡面的好像是個 EGF 的性質,考慮試試:

\(f(i)=\sum\limits_{j=0}\dfrac{a_i-j}{j!}x^j\)
\(f(i)=\sum\limits_{j=0}(\dfrac{a_i}{j!}x^j-\dfrac{x}{(j-1)!}x^{j-1})\)


\(f(i)=\sum\limits_{j=0}\dfrac{a_i-x}{j!}x^j=(a_i-x)e^x\)

然後你是要每一項乘起來:
\(F(x)=\prod\limits_{i=1}^nf(i)=e^{nx}\prod\limits_{i=1}^n(a_i-x)\)
\(F(x)=\sum\limits_{i=0}n^i\dfrac{x^i}{i!}\prod\limits_{i=1}^n(a_i-x)\)

那麼可以看出 \(\prod\limits_{i=1}^n(a_i-x)\) 是一個多項式,而且暴力算的複雜度是 \(O(n^2)\),是可以的。
那麼假設我們得出來的結果是:\(\sum\limits c_ix^i\)


\(F(x)=\sum\limits_{i=0}n^i\dfrac{x^i}{i!}\sum\limits_{i=0}c_ix^i\)

那我們答案要的自然是第 \(k\) 項:
\([x^k]F(x)=\sum\limits_{i=0}^k n^{k-i}\dfrac{x^{k-i}}{(k-i)!}c_{i}x^{i}=\sum\limits_{i=0}^k\dfrac{n^{k-i}c_i}{(k-i)!}x^k\)

然後我們帶回去:
\(E=\dfrac{k!}{n^k}*[x^k]F(x)\)
\(=\sum\limits_{i=0}^k\dfrac{k!}{n^k}\dfrac{n^{k-i}c_i}{(k-i)!}x^k\)
\(=\sum\limits_{i=0}^k\dfrac{k!c_i}{n^i(k-i)!}\)
\(=\sum\limits_{i=0}^k\dfrac{(k-i+1)(k-i+2)(...)(k)c_i}{n^i}\)
(不能直接算階乘,所以要拆開來抵消,每次在原來基礎上乘新的數)

然後最後 \(\prod a_i-E\) 即可。

程式碼

#include<cstdio>
#define ll long long
#define mo 1000000007

using namespace std;

ll n, k, a[5001], sum, c[5001], cc[5001];

void get_C() {
	c[0] = 1;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++)
			cc[j] = c[j - 1];
		for (int j = 0; j <= n; j++)
			c[j] = (c[j] * a[i] % mo - cc[j] + mo) % mo;
	}
}

ll ksm(ll x, ll y) {
	ll re = 1;
	while (y) {
		if (y & 1) re = re * x % mo;
		x = x * x % mo;
		y >>= 1;
	}
	return re;
}

int main() {
//	freopen("calculate.in", "r", stdin);
//	freopen("calculate.out", "w", stdout);
	
	scanf("%lld %lld", &n, &k); sum = 1;
	for (int i = 1; i <= n; i++) {
		scanf("%lld", &a[i]); sum = sum * a[i] % mo;
	}
	
	get_C();
	
	ll xnfn = 0, tmp = 1, invn = ksm(n, mo - 2);
	for (int i = 0; i <= n; i++) {
		(xnfn += c[i] * tmp % mo) %= mo;
		(tmp *= invn * (k - i) % mo) %= mo;
	}
	printf("%lld", (sum - xnfn + mo) % mo);
	
	return 0;
}