codeforces698C. LRU
分析
题目大意:给你一个初始为空的队列和
n
n
n个数,每一轮每个数有
p
i
p_i
pi的概率被选中。如果这个数不在队列中就把它放到队尾,否则什么都不会发生。如果当前队列大小
>
k
>k
>k,就把队首弹出去。求操作
10
0
100
100^{100}
100100之后每个数出现的概率。
10
0
100
⇔
∞
100^{100}\Leftrightarrow \infty
100100⇔∞
考虑某个数在队列的情况,那么在其之后的放的数的种类不能超过
k
−
1
k-1
k−1
如果我确定了出现的数
a
1
a
2
⋯
a
k
a_1a_2\cdots a_k
a1a2⋯ak
令
x
=
∑
p
a
k
x=\sum p_{a_k}
x=∑pak
那么每一个数可出现与可不出现的概率就是
x
+
x
2
+
x
3
+
⋯
x
∞
=
x
1
−
x
x+x^2+x^3+\cdots x^{\infty}=\frac{x}{1-x}
x+x2+x3+⋯x∞=1−xx
那么对于一个数
i
i
i,求先出现一个
i
i
i
答案就是
p
i
(
1
+
x
2
+
⋯
x
∞
)
=
p
i
1
−
x
p_i(1+x^2+\cdots x^{\infty})=\frac{p_i}{1-x}
pi(1+x2+⋯x∞)=1−xpi
但是由于这个概率包括了出现和不出现的情况,而我们要求的是大小为
0
⋯
k
0\cdots k
0⋯k的所有集合的答案,所以要容斥。
大小为
k
k
k的集合会被它的所有超集重复枚举一次,所以容斥系数就是
u
[
k
]
∑
j
=
k
+
1
u
[
j
]
C
n
−
k
j
−
k
u[k]\sum_{j=k+1} u[j]C_{n-k}^{j-k}
u[k]∑j=k+1u[j]Cn−kj−k
其中
C
n
−
k
j
−
k
C_{n-k}^{j-k}
Cn−kj−k表示
k
k
k的大小为
j
j
j的超集个数。
代码
#include<bits/stdc++.h>
const int N = 1 << 20;
int ri() {
char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int c[22][22], pw[22], cnt[N], n, k;
double u[22], p[22], sum[N];
int main() {
n = ri(); k = ri();
pw[0] = 1;
for(int i = 1;i <= n; ++i)
pw[i] = pw[i - 1] << 1;
for(int i = 0;i < n; ++i)
scanf("%lf", &p[i]), sum[pw[i]] = p[i];
c[0][0] = 1;
for(int i = 1;i <= n; c[i++][0] = 1)
for(int j = 1;j <= i; ++j)
c[i][j] = c[i - 1][j - 1] + c[i - 1][j];
for(int s = 1;s < pw[n]; ++s)
sum[s] = sum[s ^ (s&-s)] + sum[s&-s], cnt[s] = cnt[s >> 1] + (s & 1);
for(int i = k - 1; ~i; --i) {
u[i] = 1;
for(int j = i + 1; j < k; ++j)
u[i] -= u[j] * c[n - 1 - i][j - i];
}
for(int i = 0;i < n; ++i) {
if(!p[i] || p[i] == 1 || k == 1) {printf("%.9lf ", p[i]); continue;}
double ans = 0;
for(int s = 0;s < pw[n]; ++s)
if((~s & pw[i]) && cnt[s] < k)
ans += u[cnt[s]] / (1 - sum[s]);
printf("%.9lf ", p[i] * ans);
}
return 0;
}