冷滟泽的个人博客冷滟泽的个人博客

CF698C. LRU

因为 10^{100} 相对于这道题要求的精度是一个很大的数,所以每个数最终在队列中的概率会趋近于一个固定的值,也就是我们要求的答案。设 g(i,S) 表示进行 i 次操作后,队列中元素为集合 S 的概率。那么当 i 趋近无穷大时,g(i,S)=g(i+1,S),我们可以通过这个关系列出一组线性方程来解出它们。但是这个状态是有后效性的,而高斯消元的复杂度无法承受。所以我们改设状态为 f(S) 表示经过足够多次操作后,队列中元素包含子集 S 的概率。这样我们就可以 DP 求解了:只用考虑不在这个子集内的元素,每次转移时枚举这些元素并将它们加入即可。

注意需要特判概率不为 0 的元素不足 k 个的情况。

#include <cstdio>
const int MAXN=22;
const int MAXS=1100000;
double p[MAXN], f[MAXS], ans[MAXN];
int main()
{
//  freopen("C.in", "r", stdin);
//  freopen("C.out", "w", stdout);
    int n, k, cnt=0;
    scanf("%d%d", &n, &k);
    for (int i=0; i<n; i++)
    {
        scanf("%lf", &p[i]);
        if (p[i]>0) cnt++;
    }
    if (cnt<=k)
    {
        for (int i=0; i<n; i++) printf("%d ", p[i]>0);
        putchar('\n');
        return 0;
    }
    f[0]=1;
    for (int s=0; s<1<<n; s++)
    {
        if (__builtin_popcount(s)==k)
            for (int i=0; i<n; i++)
                if (s&1<<i) ans[i]+=f[s];
        double tmp=0;
        for (int i=0; i<n; i++)
            if (~s&1<<i) tmp+=p[i];
        tmp=f[s]/tmp;
        for (int i=0; i<n; i++)
            if (~s&1<<i) f[s|1<<i]+=tmp*p[i];
    }
    for (int i=0; i<n; i++) printf("%.7lf ", ans[i]);
    putchar('\n');
    return 0;
}
未经允许不得转载:冷滟泽的个人博客 » CF698C. LRU

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址