Address
Solution
- 设 f[i][j] f [ i ] [ j ] 表示第 i i 次操作后,标号为 的球的期望个数。
- 记 pre[i]=i−1(2≤i≤n),pre[1]=n p r e [ i ] = i − 1 ( 2 ≤ i ≤ n ) , p r e [ 1 ] = n 。
- 则若第
i−1
i
−
1
次操作后,标号为
j
j
的球的期望个数为 ,标号为
pre[j]
p
r
e
[
j
]
的球的期望个数为
y
y
,对于第 次操作:
- 有 xm x m 的概率取出标号为 j j 的球,操作后标号为 的个数变为 x−1 x − 1 。
- 有 ym y m 的概率取出标号为 pre[j] p r e [ j ] 的球,操作后标号为 j j 的个数变为 。
- 有 m−x−ym m − x − y m 的概率取出其它球,操作后标号为 j j 的个数不变。
- 因此可以得到转移方程:
- 展开消去同类项,得到 f[i][j]=m−1mf[i−1][j]+1mf[i−1][pre[j]] f [ i ] [ j ] = m − 1 m f [ i − 1 ] [ j ] + 1 m f [ i − 1 ] [ p r e [ j ] ] 。
- 时间复杂度 O(nK) O ( n K ) ,显然不能通过。
- 考虑矩阵优化,复杂度就被优化到了
O(n3logK)
O
(
n
3
log
K
)
,转移矩阵如下:
⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪m−1m00⋮1m1mm−1m0⋮001mm−1m⋮0⋯⋯⋯⋱⋯000⋮1m⎫⎭⎬⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪ { m − 1 m 1 m 0 ⋯ 0 0 m − 1 m 1 m ⋯ 0 0 0 m − 1 m ⋯ 0 ⋮ ⋮ ⋮ ⋱ ⋮ 1 m 0 0 ⋯ 1 m } - 尽管依然不能通过,我们会发现转移矩阵的任意次方都是这样的形式:
⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪x1xnxn−1⋮x2x2x1xn⋮x3x3x2x1⋮x4.........⋱...xnxn−1xn−2⋮x1⎫⎭⎬⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪ { x 1 x 2 x 3 . . . x n x n x 1 x 2 . . . x n − 1 x n − 1 x n x 1 . . . x n − 2 ⋮ ⋮ ⋮ ⋱ ⋮ x 2 x 3 x 4 . . . x 1 } - 因此只要计算出矩阵第一行的值,其余的行都可以随之确定,时间复杂度 O(n2logK) O ( n 2 log K ) 。
Code
仿佛这里的矩阵由于空间太大不能用结构体写
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cctype>
using namespace std;
const int N = 1005;
int n, K, pre[N]; double m;
double c[N][N], a[N], b[N][N], d[N][N], ans[N];
inline void Push(double x[N][N], double y[N][N])
{
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
x[i][j] = y[i][j];
}
inline void Join(double x[N][N], double y[N][N], double z[N][N])
{
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
z[i][j] = 0.0;
for (int k = 1; k <= n; ++k)
for (int j = 1; j <= n; ++j)
z[1][j] += x[1][k] * y[k][j];
for (int i = 2; i <= n; ++i)
for (int j = 1; j <= n; ++j)
z[i][j] = z[i - 1][pre[j]];
}
int main()
{
scanf("%d%lf%d", &n, &m, &K);
for (int i = 1; i <= n; ++i)
scanf("%lf", &a[i]);
for (int i = 2; i <= n; ++i)
pre[i] = i - 1; pre[1] = n;
double p1 = (m - 1.0) / m, p2 = 1.0 / m;
for (int j = 1; j <= n; ++j)
b[j][j] = p1, b[pre[j]][j] = p2;
for (int i = 1; i <= n; ++i) c[i][i] = 1;
while (K)
{
if (K & 1)
Join(b, c, d), Push(c, d);
Join(b, b, d); Push(b, d); K >>= 1;
}
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
ans[j] += a[i] * c[i][j];
for (int i = 1; i <= n; ++i)
printf("%.3lf\n", ans[i]);
return 0;
}