袋子里有n种球,第i种颜色有ai 个
每次操作随机选两个球,将第一个球染成第二个球的颜色
求全部颜色变成相同的期望次数
1≤n≤2500,1≤ai≤105
Links
CF850F
Solution
发现直接dp不好设状态,因为确定了最终颜色之后,剩下的颜色具体是什么无关紧要,于是考虑设计与最终颜色有关的状态,即先枚举最终颜色
设f[i]表示当前有i个钦定颜色的球,到结束的期望时间,记s=∑i=1nai
f[i]=p(f[i−1]+f[i+1])+(1−2p)f[i]+E其中p=s(s−1)i(s−i),为选出一个钦定的球和一个其他颜色球的概率,变多变少都是这个概率
而E=si,为产生贡献的期望时间
为什么这里E≠1呢?
一句话概括,就是当前钦定的颜色必须存在,即个数不能为0
具体地,当前的i个球减少与增加一个的概率均等,最后目标是要在到达0之前到达s
这实际上就是一个赌徒破产问题
解一下方程就能得到从i个球在到0个球之前到s个球的概率为si,因此期望步数就为si
或者也可以这么理解:只有si的概率到达的后续状态是合法的
仔细思考一下,这个期望与平时所求的期望不同的本质原因是,这个期望的初始状态和终止状态比较奇怪,可能到达一些不合法的状态,这才导致了产生贡献的期望不同
化简一下得到
2f[i]=f[i−1]+f[i+1]+s−is−1边界情况:
f[1]−f[2]f[s]=−f[1]+1=0按照套路化一下
f[i]−f[i+1]=f[i−1]−f[i]+s−is−1=−f[1]+j=1∑is−js−1继续化简得到
f[1]=f[1]−f[s]=i=1∑s−1f[i]−f[i+1]=i=1∑s−1(−f[1]+j=1∑is−js−1)=−i=1∑s−1f[1]+i=1∑s−1j=1∑is−js−1=−(s−1)f[1]+i=1∑s−1s−is−1⋅(s−i)=(1−s)f[1]+(s−1)2于是顺利得到
f[1]=s(s−1)2直接递推即能得到所有f,答案就是∑i=1nf[ai]
Summary
这道题首先要会通过剔除不必要的信息来巧妙设dp状态;一个比较难的点是中间应用赌徒破产模型那里,剩下的都是套路
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| #include <bits/stdc++.h>
#define x first #define y second #define y1 Y1 #define y2 Y2 #define mp make_pair #define pb push_back
using namespace std;
typedef long long LL; typedef pair <int, int> pii;
template <typename T> inline int Chkmax (T &a, T b) { return a < b ? a = b, 1 : 0; } template <typename T> inline int Chkmin (T &a, T b) { return a > b ? a = b, 1 : 0; } template <typename T> inline T read () { T sum = 0, fl = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') fl = -1; for (; isdigit(ch); ch = getchar()) sum = (sum << 3) + (sum << 1) + ch - '0'; return sum * fl; }
inline void proc_status () { ifstream t ("/proc/self/status"); cerr << string (istreambuf_iterator <char> (t), istreambuf_iterator <char> ()) << endl; }
const int Maxn = 1e5 + 100, Mod = 1e9 + 7;
int N, S, A[Maxn], Max; int f[Maxn];
inline int Pow (int a, int b) { int ans = 1; for (int i = b; i; i >>= 1, a = (LL)a * a % Mod) if (i & 1) ans = (LL)ans * a % Mod; return ans; }
inline void Add (int &a, int b) { a += b; if (a >= Mod) a -= Mod; }
inline void Solve () { f[1] = (LL)(S - 1) * (S - 1) % Mod * Pow(S, Mod - 2) % Mod;
for (int i = 1; i < Max; ++i) f[i + 1] = ((2ll * f[i] - f[i - 1] - (LL)(S - 1) * Pow(S - i, Mod - 2) % Mod) % Mod + Mod) % Mod;
int ans = 0; for (int i = 1; i <= N; ++i) Add (ans, f[A[i]]); cout<<ans<<endl; }
inline void Input () { N = read<int>(); for (int i = 1; i <= N; ++i) A[i]= read<int>(), S += A[i], Chkmax(Max, A[i]); }
int main() { #ifndef ONLINE_JUDGE freopen("F.in", "r", stdin); freopen("F.out", "w", stdout); #endif Input(); Solve(); return 0; }
|