CF1349D Slime and Biscuits
思路
果然对期望DP
还没完全理解透啊,这道题不得不说太妙了
这道题的关键就是:设而不求,转化答案,破除界限
我们设以下变量:
\(E(x)\) 表示当结束时所有饼干都到 \(x\) 手上的期望,即在此之前饼干不曾集中在一个人手上
\(Es(x)\) 表示所有饼干集中到 \(x\) 手上才结束的期望,即之前可以存在饼干集中在除 \(x\) 的某一个人身上
\(P_x\) 表示结束时,所有饼干都在 \(x\) 手上的概率
\(C\) 表示当饼干已经集中在某个人手上时,将所有饼干集中到另一个人的期望步数
我们可以发现,其实 \(Es(x)=E(x)+(1-P_x)\times C+\sum_{i=1\ and\ i\not=x}^n E(i)=\sum_{i=1}^nE(x)+(1-P_x)\times C\),
而答案 \(ans=\sum_{i=1}^n E(i)\)
结合两个式子,有 \(ans=Es(x)-(1-P_x)\times C\)
显然,因为结束时所有饼干一定会集中在某个人的手里,因此有 \(\sum_{i=1}^nP(i)=1\)
所以 \(\sum_{i=1}^n(Ex(i)-(1-P_i)\times C)=\sum_{i=1}^n Ex(i)-(n-1)\times C=n\times ans\)
这里我们成功将 \(E(x)\) 消去,转化为求限制没那么紧的 \(Es(x)\) 和 \(C\)
我们设 \(f_x\) 表示当某个人手上有 \(x\) 个饼干时,所有饼干都集中到他手里的期望
那么显然 \(Es(x)=f_{a[x]}, C=f_0\) (\(a[x]\) 表示 \(x\) 开始有的饼干数)
我们根据期望DP
的套路就可以有以下表达式:(\(m\) 表示饼干的总数)
到这里其实就可以直接用带状矩阵消元
用 \(O(n)\) 的时间做出来了,空间压缩一下数组就可以实现
但硅基生物大佬们注意到转移方程中的所有系数和都为 \(1\),于是他们就开始了魔法操作
我们令 \(g_i=f_i-f_{i-1}\),显然 \(f_i=\sum_{k=i}^m g_k,\ 0<i<m\),以及 \(g_m=0\)
那么根据那条一般的转移方程,我们有
通过拆拆拆消消消合并合并合并,我们可以化成一下式子:
这是因为等式两边的 \(\sum_{k=i+1}^m\) 系数都为 \(1\) ,然后就消掉了
最后可以化成这个递推式:
至于 \(g_0\),用第二条转移方程,相同方法可以直接算出 \(g_0=n-1\)
最后记得答案是 \(ans=\frac{\sum_{i=1}^nf_{a[i]}-(n-1)\times C}{n}\)
代码
#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define LL long long
inline int reads()
{
int sign = 1, re = 0; char c = getchar();
while(c < '0' || c > '9'){if(c == '-') sign = -1; c = getchar();}
while('0' <= c && c <= '9'){re = re * 10 + (c - '0'); c = getchar();}
return sign * re;
}
const int mod = 998244353;
inline int add(int a, int b) {return a + b >= mod ? a + b - mod : a + b;}
inline int mul(int a, int b) {return 1ll * a * b % mod;}
inline int sub(int a, int b) {return a - b < 0 ? a - b + mod : a - b;}
inline int fast_pow(int a, int b)
{
int re = 1;
while(b)
{
if(b & 1) re = mul(re, a);
a = mul(a, a);
b >>= 1;
}
return re;
}
inline int inv(int a) {return fast_pow(a, mod - 2);}
int n, m, a[100005], ans;
int f[300005], g[300005];
signed main()
{
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
#endif
n = reads();
for(int i = 1; i <= n; i++)
a[i] = reads(), m += a[i];
g[0] = n - 1;
for(int i = 1; i < m; i++)
{
int Inv = inv(m - i);
g[i] = add(mul(mul(m, n - 1), Inv), mul(mul(mul(i, n - 1), Inv), g[i - 1]));
}
for(int i = m - 1; i >= 0; i--)
f[i] = add(g[i], f[i + 1]);
for(int i = 1; i <= n; i++)
ans = add(ans, f[a[i]]);
ans = sub(ans, mul(f[0], n - 1));
ans = mul(ans, inv(n));
printf("%d", ans);
return 0;
}