codeforces698C. LRU 容斥原理 概率Dp 狀壓Dp

codeforces698C. LRU

題目鏈接

分析

題目大意:給你一個初始爲空的隊列和nn個數,每一輪每個數有pip_i的概率被選中。如果這個數不在隊列中就把它放到隊尾,否則什麼都不會發生。如果當前隊列大小>k>k,就把隊首彈出去。求操作100100100^{100}之後每個數出現的概率。
100100100^{100}\Leftrightarrow \infty
考慮某個數在隊列的情況,那麼在其之後的放的數的種類不能超過k1k-1
如果我確定了出現的數a1a2aka_1a_2\cdots a_k
x=pakx=\sum p_{a_k}
那麼每一個數可出現與可不出現的概率就是
x+x2+x3+x=x1xx+x^2+x^3+\cdots x^{\infty}=\frac{x}{1-x}
那麼對於一個數ii,求先出現一個ii
答案就是pi(1+x2+x)=pi1xp_i(1+x^2+\cdots x^{\infty})=\frac{p_i}{1-x}
但是由於這個概率包括了出現和不出現的情況,而我們要求的是大小爲0k0\cdots k的所有集合的答案,所以要容斥。
大小爲kk的集合會被它的所有超集重複枚舉一次,所以容斥係數就是u[k]j=k+1u[j]Cnkjku[k]\sum_{j=k+1} u[j]C_{n-k}^{j-k}
其中CnkjkC_{n-k}^{j-k}表示kk的大小爲jj的超集個數。

代碼

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章