Codeforces Round #363 (Div. 1) C LRU

来源:互联网 发布:淘宝模板什么意思 编辑:程序博客网 时间:2024/06/12 01:15

原题网址:http://codeforces.com/contest/698/problem/C

这里写图片描述
这里写图片描述
法一(cf上的题解):把问题倒过来想。假设从最晚访问的元素开始向前加元素,加到k个为止。求第i个出现的概率。这样就不同考虑从缓存区有文件出来了。
将20个元素压位,dp[mask]记录mask状态(由 0/1 表示的状态)出现的可能性。当1<< i在mask中为0,也就是第i个元素没有出现过时,dp[mask]可以转移到dp[mask+(1<< i)],表示第i个元素在mask中所有已存在元素之前加入,转移时要乘上概率p(i)/(p(i)+p(x)),其中p(x)是除了mask中元素和i元素的其他元素出现的概率,这个式子的含义是在除了已经在mask中出现的元素中,i第一个出现,所以用i的概率除以还没被选入mask中元素的概率和。最后答案只要统计一下有k个元素且第i个元素存在的状态的概率和。
要注意的是如果有概率为0的要直接去掉,因为这个元素根本不能进缓存,如果没有去掉,答案肯定是会出问题的。而且去掉这些元素后,要保证k<=n。

Codeforces题解原文:

The key idea of this problem is to consider the process backwards. If we fix some very far moment of time (i.e 10100, as in the problem statement), and now look operation in the reverse direction, the problem becomes the following: element is chosen with probability pi and if it is not present in the current set, we add it there. We stop if there are k elements in the set. What is the probability the i-th element will be present in the set at the end? This is now much easier to solve, as we should only add new elements, without removing anything. As a result, we do not have to store the order elements were added and are only interested in the set.

Calculate the following dynamic programming: dp[mask] = the probability to have the set given by some mask at some point of the
process. We try to move to all newmask that are equal to mask + (1«i) for all i not present in mask.

Now, to obtain the answer for some particular element we just have to pick all mask of size k that contain this element.

One detail we have to deal with is that zero probability pi = 0 is allowed. Remove all such elements from the set and reduce the value of k if necessary.

下面的程序是按照dp[mask]从dp[mask-(1<< i)]转移过来写的,实质相同:

const  eps=1e-6;var  f,sp:array[0..1050000] of extended;  tot:array[0..1050000] of longint;  p,ans:array[0..20] of extended;  sit:array[0..20] of longint;  n,k,i,j,cn:longint;  t:extended;procedure calc(x:longint);  var    s:extended;    i:longint;  begin    s:=0;tot[x]:=0;    for i:=1 to n do      if (x and (1<<(i-1))>0)        then begin s:=s+p[i];inc(tot[x]);end;    if tot[x]=1 then f[x]:=s;    sp[x]:=s;  end;function dp(s:longint):extended;  var    i:longint;  begin    if f[s]>-1 then exit(f[s]);    f[s]:=0;    for i:=1 to n do      if (s and (1<<(i-1))>0)and(1-sp[s]+p[i]>0)        then f[s]:=f[s]+dp(s-(1<<(i-1)))*p[i]/(1-sp[s]+p[i]);    exit(f[s]);  end;begin  readln(n,k);  cn:=n;n:=0;  fillchar(sit,sizeof(sit),0);  for i:=1 to cn do    begin      read(t);      if abs(t)>eps then        begin          inc(n);          p[n]:=t;          sit[i]:=n;        end;    end;  if n<k then k:=n;   for i:=1 to 1<<n-1 do    begin      f[i]:=-1;      calc(i);    end;  fillchar(ans,sizeof(ans),0);  for i:=1 to 1<<n-1 do    if tot[i]=k then      for j:=1 to n do        if i and (1<<(j-1))>0          then ans[j]:=ans[j]+dp(i);  ans[0]:=0;            for i:=1 to cn-1 do    write(ans[sit[i]]:0:10,' ');  writeln(ans[sit[cn]]:0:10);end.

法二:考虑如果i在最终缓存区中,设i后加入的元素集合为T,只要把|T| < k的情况的概率统计出来就可以了。
对于一个元素x,引入一个函数sum[i],表示|T|<=i的概率和。sum[i]=∑p[x]/(p[x]+p[y]),枚举y,y是从n个元素中取出i和s个异于i的元素后的集合的元素的概率和,这个式子的含义是在除了s个异于i的元素,i第一个出现,所以用i的概率除以那s个元素的概率和。
引入另一个函数ans[i],表示|T|=i的概率,要用容斥去除一些多余概率。ans[i]=sum[i]-∑j=0..i-1 C(n-1-j,i-j) * ans[j],因为j个元素以及固定了,再去掉i元素也已经取了,从剩下的n-1-j个元素中选出i-j个元素有C(n-1-j,i-j)中可能,C(n-1-j,i-j) * ans[j] 可以把sum[i]中|T|=j的情况去除。
统计答案时只要把ans[0..k-1]加和即可。

var  c:array[0..20,0..20] of longint;  p,sum,ans:array[0..20] of extended;  n,k,i,j,l:longint;  fans:extended;procedure dfs(dep,now,s:longint;po:extended);  begin    if dep>n then      begin        if p[now]+po<>0          then sum[s]:=sum[s]+p[now]/(p[now]+po);        exit;      end;    if dep=now then      begin        dfs(dep+1,now,s,po);        exit;      end;    dfs(dep+1,now,s,po+p[dep]);    dfs(dep+1,now,s+1,po);  end;begin  read(n,k);  for i:=1 to n do read(p[i]);  for i:=0 to 20 do c[i][0]:=1;  for i:=0 to 20 do c[i][i]:=1;  for i:=0 to 20 do    for j:=1 to i-1 do      c[i][j]:=c[i-1][j-1]+c[i-1][j];       for i:=1 to n do    begin      fillchar(sum,sizeof(sum),0);      dfs(1,i,0,0);      for j:=0 to k-1 do        begin          ans[j]:=sum[j];          for l:=0 to j-1 do            ans[j]:=ans[j]-c[n-1-l][j-l]*ans[l];        end;      fans:=0;        for j:=0 to k-1 do        fans:=fans+ans[j];      write(fans:0:12);      if i<n then write(' ');    end;  writeln;  end.
0 0