天天看点

【JZOJ100004】【NOI2017模拟.4.1】 Dice任务解法代码

任务

【JZOJ100004】【NOI2017模拟.4.1】 Dice任务解法代码
【JZOJ100004】【NOI2017模拟.4.1】 Dice任务解法代码

解法

我们分开考虑每个骰子的贡献;

设 Xi 表示第 i 个骰子的点数。

显然Ans1=∑ni=1E[Xi],

又 E[Xi]=∑6j=1j∗P[Xi=j] ,

但题目要求我们不能连续两次投相同的骰子;

所以 P[Xi=j]=∑6k=1P[Xi−1=k]∗P[Xi=j|Pi−1=k] ,其中 i>1

P[Xi=j|Pi−1=k]={0,pj+pk∗P[Xi=j|Pi−1=k],j=kj!=k

所以 P[Xi=j|Pi−1=k]=pj1−pk

我们知道 E[Ans1] 后,要求 Ans2=E[(Ans1−E[Ans1])2] ;

也即 Ans2=E[(∑Xi−E[Ans1])2]

由于 E[Ans1] 是常量,我们先让 Xi−E[ans1]n ,则有 Ans2=E[(∑X′i)2]

所以

E[Ans2]=E[(∑X′i)2]=E[∑i=1n∑j=1nXi∗Xj]=E[∑i=1n(Xi)2]+2∗E[∑i=1n∑j=i+1nXi∗Xj]

容易算出 E[∑ni=1(Xi)2] ,

我们考虑 E[∑ni=1∑nj=i+1Xi∗Xj] ;

E[∑i=1n∑j=i+1nXi∗Xj]=∑i=1n∑j=i+1n∑k=16∑l=16k∗l∗P[Xi=k]∗P[Xj=l|Xi=k]=∑k=16∑l=16k∗l∗∑i=1n∑j=i+1nP[Xi=k]∗P[Xj=l|Xi=k]=∑k=16∑l=16k∗l∗∑i=1nP[Xi=k]∗∑j=i+1nP[Xj=l|Xi=k]=∑k=16∑l=16k∗l∗∑i=1nP[Xi=k]∗∑j=1n−i+1P[Xj=l|X1=k]

由于 ∑n−i+1j=1P[Xj=l|X1=k] 只与三元有关,所以可以预处理。

代码

#include<iostream>
#include<algorithm>
#include<math.h>
#include<stdio.h>
#include<string.h>
#define ll long long
#define db double
using namespace std;
const char* fin="dice.in";
const char* fout="dice.out";
const int inf=;
const int maxn=;
int n,m,i,j,k,l;
db p[],ans1=,ans2=,oo;
db f[maxn][],g[][][maxn],h[][][maxn];
int main(){
    freopen(fin,"r",stdin);
    freopen(fout,"w",stdout);
    for (i=;i<=;i++) scanf("%lf",&p[i]);
    scanf("%d",&n);
    for (i=;i<=n;i++)
        for (j=;j<=;j++)
            if (i==) f[i][j]=p[j];
            else for (k=;k<=;k++) 
                if (j!=k) f[i][j]+=f[i-][k]*(p[j]/(-p[k]));
    for (i=;i<=n;i++) for (j=;j<=;j++) ans1+=f[i][j]*j;
    printf("%lf\n",ans1);
    for (i=;i<=;i++) for (j=;j<=;j++){
        if (i!=j){
            g[i][j][]=p[j]/(-p[i]);
            h[i][j][]=g[i][j][];
        }
    }
    for (k=;k<=n;k++){
        for (i=;i<=;i++) for (j=;j<=;j++){
            for (l=;l<=;l++){
                if (j!=l){
                    g[i][j][k]+=g[i][l][k-]*(p[j]/(-p[l]));
                }
                h[i][j][k]=g[i][j][k]+h[i][j][k-];
            }
        }
    }
    db C=ans1/n;
    for (k=;k<=;k++)
        for (l=;l<=;l++)
            for (i=;i<n;i++)
                ans2+=(k-ans1/n)*(l-ans1/n)*f[i][k]*h[k][l][n-i+];
    ans2*=;
    for (i=;i<=n;i++) for (j=;j<=;j++) ans2+=(j-ans1/n)*(j-ans1/n)*f[i][j];
    printf("%lf\n",ans2);
    return ;
}