bzoj2720 [Violet 5]列队春游 (期望概率)
原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=2720
题意:
数据范围
保证 1≤n≤300,1≤hi≤1000
题解:
这题理解了好久,O(n^3)和O(n^2)好麻烦
以下是O(n)的做法:
对于一个高度为h的学生i,比他高度小的有s个,高度为h的有num[h]个。
我们需要计算对于这个高i的学生,他的期望视野是多少。
可以看到,一个学生的视野就是 前面比他矮的个数+1
我们暂且不看那个+1,先来算前面期望比他矮的个数
若有学生j∈s集合,即j比i矮,设f[i]为前面期望比i矮的个数,d[j]是j对i的贡献,p[j]是j对i有贡献的概率。
p[j]=(j对i有贡献的排列数)/(n个学生的总排列数)
则
d[j]=1*p[j]
那么
f[i]=sum((j∈s)d[j])
那么p[j]怎么算呢?
要满足j对i有贡献,那么j与i之间只能有其他属于s集合的学生,有s-1个。
先忽略这s-1个学生,现在还剩n-s+1个学生。那么这些学生的排列中,j刚好在i前一个的排列有: (n-s)! 个
而剩下(s-1)个比i矮的学生的分散在上述排列中的排列有
(n-s)!* A(n,s-1)
=( (n-s)!* n! ) / (n-s+1)!
=n!/(n-s+1)
只有在这些排列中,i与j之间都不会出现比i高或和i一样高的,即j对i有贡献。
p[j]
=(n!/(n-s+1))/ n!
=1/( n-s+1)
f[i]=s*1/( n-s+1)= s/( n-s+1)
别忘了视野=f[i]+1
因为高度为h的有num个
所以对这num[h]个人的期望=num[h]*( (s/( n-s+1)) +1 ) )
=num[h]*s/(n-s+1) + num[h]
所以
ans=sum((i∈[1,1000]) num[i]*s/(n-s+1) + num[i])
处理完后,s每次加上当前的num[i]就行
//赞叹一发自己的机智
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1005;
int n,num[N],h,s=0;
double ans=0.0;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&h);
num[h]++;
}
for(int i=1;i<=1000;i++)
{
if(num[i])
ans+=(double)(num[i]*s)/(n-s+1)+double(num[i]);
s+=num[i];
}
printf("%0.2lf\n",ans);
return 0;
}