[ABC008C] コイン
一道有趣的期望题。
解法
题中给出 n n n 枚硬币,每枚硬币上都有一个正整数,记作 a i a_i ai。下面为了方便叙述,我们将 n n n 个硬币进行标号,依次为 1 1 1 到 n n n。
现在要将这 n n n 枚硬币进行全排列,共有 n ! n! n! 种排列方法,求出全部翻转后最终正面朝上(初始状态是全部正面朝上)的硬币数量的期望值(输出浮点数)。翻转规则如下:
-
从最左端开始向右逐个扫过。
-
当扫到第 i i i 个硬币时,我们将第 i i i 个硬币右侧且硬币上的数字是 a i a_i ai 倍数的硬币进行一次翻转。
注意:本题要注意整数和浮点数间的转换。
部分分解法
对于 1 ≤ n ≤ 8 1 \le n \le 8 1≤n≤8 的情况,我们可以全排列出所有情况,对于每一种情况暴力翻转,求出每种情况硬币正面朝上的数量,从而算出所有情况硬币正面朝上的数量的总和,除以 n ! n! n! 浮点数输出期望即可。
满分解法
考虑到有一个点的数据范围是 1 ≤ n ≤ 100 1 \le n \le 100 1≤n≤100,这种暴力的做法是不能得到满分的。考虑到我们正在对每种排列情况进行计数,我们可以换一个计数方式,即考虑每个硬币在所有情况中正面朝上的概率的总和。
观察样例我们可以得到:对于编号为 i i i 的硬币,能使之翻转的硬币只能是 a i a_i ai 的约数所在的硬币。所以如果对于 a j ∤ a i a_j \nmid a_i aj∤ai,在任意排列中无论编号为 j j j 的硬币在编号为 i i i 的硬币的任何方向,都不会影响编号为 i i i 的硬币的翻转情况。所以我们在计数编号为 i i i 的硬币时可以忽略这些不是该硬币约数的硬币。现在即求对于编号为 i i i 的硬币和它的 x i x_i xi 个约数,在这 x i + 1 x_i +1 xi+1 个数的全排列中有多少种情况编号为 i i i 的硬币在这列数中排在第奇数位(即该数前面有偶数个约数)的概率。对于编号为 i i i 的硬币,计算公式为:$\dfrac{ x_i! \times \lfloor x_i / 2 + 1 \rfloor }{ (x_i + 1)! } = \dfrac{ \lfloor x_i / 2 + 1 \rfloor }{ x_i + 1 } $,解释如图:
代码
#include<bits/stdc++.h>
using namespace std;
int n,a[110];
double ans;
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1,cnt=0;i<=n;i++,cnt=0)
{
for(int j=1;j<=n;j++) if(a[i]%a[j]==0) cnt++;
cnt--;//把自己排除
ans+=(cnt/2+1)*1.0/(cnt+1);
}
cout<<fixed<<setprecision(6)<<ans<<endl;//注意,因为这道题是早期 AT 的题,最后一定要多输出一个空格
return 0;
}