【YBT2022寒假Day6 A】【luogu CF891E】隨機減法 / Lust(EGF)
隨機減法 / 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;
}