【Codeforces Round 363 (Div 2) E】【概率DP 期望DP 逆推等价法】LRU Cache替换LRU原则超多步数后每个数据在Cache中的概率

E. LRU
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

While creating high loaded systems one should pay a special attention to caching. This problem will be about one of the most popular caching algorithms called LRU (Least Recently Used).

Suppose the cache may store no more than k objects. At the beginning of the workflow the cache is empty. When some object is queried we check if it is present in the cache and move it here if it's not. If there are more than k objects in the cache after this, the least recently used one should be removed. In other words, we remove the object that has the smallest time of the last query.

Consider there are n videos being stored on the server, all of the same size. Cache can store no more than k videos and caching algorithm described above is applied. We know that any time a user enters the server he pick the video i with probability pi. The choice of the video is independent to any events before.

The goal of this problem is to count for each of the videos the probability it will be present in the cache after 10100 queries.

Input

The first line of the input contains two integers n and k (1 ≤ k ≤ n ≤ 20) — the number of videos and the size of the cache respectively. Next line contains n real numbers pi (0 ≤ pi ≤ 1), each of them is given with no more than two digits after decimal point.

It's guaranteed that the sum of all pi is equal to 1.

Output

Print n real numbers, the i-th of them should be equal to the probability that the i-th video will be present in the cache after 10100queries. You answer will be considered correct if its absolute or relative error does not exceed 10 - 6.

Namely: let's assume that your answer is a, and the answer of the jury is b. The checker program will consider your answer correct, if .

Examples
input
3 1
0.3 0.2 0.5
output
0.3 0.2 0.5 
input
2 1
0.0 1.0
output
0.0 1.0 
input
3 2
0.3 0.2 0.5
output
0.675 0.4857142857142857 0.8392857142857143 
input
3 3
0.2 0.3 0.5
output
1.0 1.0 1.0 

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 0, M = 0, Z = 1e9 + 7, ms63 = 0x3f3f3f3f;
int one[1 << 20];	//one[i]表示i的二进制中含有的1的数量
int n, k;
int o[20];			//o[i]表示第i(i为新编号)个数据对应的原始编号
double p[20];		//p[i]表示第i(i为新编号)个数据被选中的概率
double ans[20];		//ans[i]表示最终第i个数据留在Cache中的概率
double f[1 << 20];
int main()
{
	for (int i = 0; i < (1 << 20); ++i)
	{
		for (int j = 0; j < 20; ++j)one[i] += i >> j & 1;
	}
	while (~scanf("%d%d", &n, &k))
	{
		int m = 0;
		for (int i = 0; i < n; ++i)
		{
			double pp; scanf("%lf", &pp);
			if (pp < 1e-9)continue;
			o[m] = i;
			p[m++] = pp;
		}
		gmin(k, m);

		MS(ans, 0);
		MS(f, 0); f[0] = 1;
		int top = 1 << m;
		for (int i = 0; i < top; ++i)
		{
			double out = 1;
			for (int j = 0; j < m; ++j)if (i >> j & 1)
			{
				out -= p[j];
				if (one[i] == k)ans[o[j]] += f[i];
			}
			for (int j = 0; j < m; ++j)if (~i >> j & 1)
			{
				f[i | 1 << j] += f[i] * p[j] / out;
			}
		}
		for (int i = 0; i < n; ++i)printf("%.10f ", ans[i]); puts("");
	}
	return 0;
}
/*
【trick&&吐槽】
这道题很奇怪,比赛的时候没做出来。
要不是我分低,差点就跌分了233 TwT

【题意】
你知道Cache吗?
我们Cache中有k个数据存放位置和n个可能需要存放的数据(1<=k<=n<=20)

然后,对于每次询问。
	每份数据都存在着一个需要被放到Cache中的概率p[]。当然∑{p[]}==1
	这个概率与询问无关,对于每次询问,被放到Cache中的概率都是一个定值。

然而,Cache的存放空间,可能比数据量要小。
也就是说,有可能Cache的存放空间是不够的,也就是说,当我们把Cache中的一个数据放进去的时候,
可能要把另外一份数据移除。
那我们移走哪份呢?我们移走距离上次数据使用,时间最久的那份。

现在我们做了额1e100次询问。
问你,这个时候,每份数据在Cache中的概率是多少。
(显然,这个概率之和=k)

【类型】
DP

【分析】
这道题的解决,首先需要我们对问题的转化——
LRU的替代规则,似乎使得我们要记录每个点的最后询问时间,这个有些难于搞定。

但是,我们换一种思路就好了。
我们倒着思维,(假设p[]>0都成立)
因为仅过了1e100次询问,所以最后,这个集合中一定会有k个元素。

有若干个mask集合,其恰好有k个元素。
对于这样的集合,其都可以作为最后状态的终点。
我们只要枚举所有"恰好有k个元素"的集合,最后以每一个这样的集合为收场,都对应着一个概率
对于数据i而言,∑(probility[maskj],maskj has i)就是ans[i]了。

于是,问题转化为,如何求probility[mask]。
这个probility,
我们可以按照时间顺序,正着加元素,不过这样不知道前驱状态,思考起来有些难于下手。
我们不妨逆着时间顺序,从最后的结束状态,倒着往回加元素(只要使得元素个数恰好k个就好了嘛)

状态转移其实就只有一个公式——
f[i | 1 << j] += f[i] * p[j] / out;

意味着:
对于每个状态i,它加入一个新的元素,使得集合状态发生变化。
那变化的概率是什么呢?p[j] / out
变化之后变成了什么呢?f[i | 1 << j]

为什么我们不用考虑一个集合向其自身的转移呢?
因为没有意义。转移次数太多了,其向自身转移,最后还是要转移出去,也就是说,其最终转移出去的概率是100%。
于是我们不妨不考虑其向自身的转移(因为这样的转移只影响转移步长,并不影响转移的目标与结果)

【时间复杂度&&优化】
O(2 ^ 20 * 20)

*/


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值