题意:
有n个人进行m次比赛,每次比赛有一个排名,最后的排名是把所有排名都加起来然后找到比自己的分数绝对小的人数加一就是最终排名。
给了其中一个人的所有比赛的名次。求这个人最终排名的期望。
思路:
渣渣的第一道概率DP...弱渣。
dp[i][j]代表进行了i轮比赛之后得分为j的人数的期望,可以知道dp[i][j]来自于dp[i-1][j-m...j-1]的期望的人数乘以概率。这里就是前缀和,用树状数组进行优化...
但是要注意,转移的时候一定要排除已经知道的名次的影响,不能用已知名次来转移...
这题卡了好多天...终于开窍...
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; double tree[100050]; double dp[100050]; int me[105];//目标选手每场比赛的名次 int n,m; int ss;//目标选手的名次和 double findans(int tar) { double sum=0; while(tar>0) { sum+=tree[tar]; tar-=tar&(-tar); } return sum; } void updat(int tar,double num) { while(tar<ss) { tree[tar]+=num; tar+=tar&(-tar); } } int main() { scanf("%d%d",&n,&m); memset(dp,0,sizeof(dp)); memset(tree,0,sizeof(tree)); ss=0; for(int i=1;i<=n;i++) { scanf("%d",&me[i]); ss+=me[i]; } if(m==1) {printf("1.0000000000000000\n");return 0;} for(int j=1;j<=m;j++) { dp[j]=1; } dp[me[1]]=0; for(int j=1;j<ss;j++) { updat(j,dp[j]); } for(int i=2;i<=n;i++) { memset(dp,0,sizeof(dp)); for(int j=i;j<ss;j++) { int k; dp[j]+=findans(j-1)/(m-1); k=j-m-1; if(k>=i-1) dp[j]-=findans(k)/(m-1); if(j-i+1>=me[i]) { dp[j]-=findans(j-me[i])/(m-1); if(j-me[i]-1>=i-1) { dp[j]+=findans(j-me[i]-1)/(m-1); } } } memset(tree,0,sizeof(tree)); for(int j=i;j<ss;j++) { updat(j,dp[j]); } } printf("%.16lf\n",findans(ss-1)+1); }