题意
题解
令所有的书魔法属性都一样时为终止状态,考虑统计各个终止状态的贡献。固定终止状态的属性值
a
a
a,那么只关注属性为
a
a
a 与属性不为
a
a
a 的书籍即可。
f
k
f_k
fk 代表当前属性为
a
a
a 的书籍有
k
k
k 本,令
p
=
k
(
n
−
k
)
n
(
n
−
1
)
p = \frac{k(n-k)}{n(n-1)}
p=n(n−1)k(n−k) 考虑任意一步状态转移,则
f
k
f_{k}
fk 向
f
k
−
1
,
f
k
+
1
f_{k-1}, f_{k+1}
fk−1,fk+1 转移概率相同。其中
f
n
=
0
f_n = 0
fn=0,为了仅考虑
f
k
→
f
n
f_k\rightarrow f_n
fk→fn 的贡献,令
f
0
=
0
f_0 = 0
f0=0。那么
f
k
=
p
(
f
k
−
1
+
f
k
+
1
)
+
(
1
−
2
p
)
f
k
+
k
n
f_k = p(f_{k-1} + f_{k+1}) + (1-2p)f_k + \frac{k}{n}
fk=p(fk−1+fk+1)+(1−2p)fk+nk 最后一项代表
f
k
→
f
n
f_k\rightarrow f_n
fk→fn 的概率,问题等价于
x
x
x 轴上某点
k
k
k 向左右移动的概率相同,求不抵达
0
0
0 的情况下抵达
n
n
n 的状态。
此时可以列出 n n n 个 n n n 元一次方程。系数矩阵只有主对角线与其上下两条对角线非零,那么可以每一行 i i i 只对 i + 1 i+1 i+1 行消元,最后回代求解。总时间复杂度 O ( n ) O(n) O(n)。
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
const int MAXN = 2E3 + 5;
string S;
int cnt[128];
double B[MAXN][MAXN], f[MAXN];
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> S;
int n = S.size();
B[0][0] = 1, B[n][n] = 1;
for (int k = 1; k < n; ++k)
{
B[k][k - 1] = -1;
B[k][k] = 2;
B[k][k + 1] = -1;
B[k][n + 1] = 1.0 * (n - 1) / (n - k);
}
for (int k = 0; k < n; ++k)
{
B[k][k + 1] /= B[k][k];
B[k][n + 1] /= B[k][k];
B[k + 1][k + 1] -= B[k + 1][k] * B[k][k + 1];
B[k + 1][n + 1] -= B[k + 1][k] * B[k][n + 1];
}
f[n] = B[n][n + 1];
for (int k = n - 1; k >= 0; --k)
f[k] = B[k][n + 1] - B[k][k + 1] * f[k + 1];
double res = 0;
for (int i = 0; i < n; ++i)
++cnt[S[i]];
for (int i = 0; i < 128; ++i)
res += f[cnt[i]];
cout << fixed << setprecision(1) << res << '\n';
return 0;
}