codeforces698C. LRU
分析
题目大意:给你一个初始为空的队列和个数,每一轮每个数有的概率被选中。如果这个数不在队列中就把它放到队尾,否则什么都不会发生。如果当前队列大小,就把队首弹出去。求操作之后每个数出现的概率。
考虑某个数在队列的情况,那么在其之后的放的数的种类不能超过
如果我确定了出现的数
令
那么每一个数可出现与可不出现的概率就是
那么对于一个数,求先出现一个
答案就是
但是由于这个概率包括了出现和不出现的情况,而我们要求的是大小为的所有集合的答案,所以要容斥。
大小为的集合会被它的所有超集重复枚举一次,所以容斥系数就是
其中表示的大小为的超集个数。
代码
#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;
}