codeforces698C. LRU 容斥原理 概率Dp 状压Dp

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 k1
如果我确定了出现的数 a 1 a 2 ⋯ a k a_1a_2\cdots a_k a1a2ak
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=1xx
那么对于一个数 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)=1xpi
但是由于这个概率包括了出现和不出现的情况,而我们要求的是大小为 0 ⋯ k 0\cdots k 0k的所有集合的答案,所以要容斥。
大小为 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]Cnkjk
其中 C n − k j − k C_{n-k}^{j-k} Cnkjk表示 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值