牛客第9场-E 概率dp

把概率dp当数学题做推公式,也是我太菜了。最重要是我还津津有味推了一下午。

刚一开始我想到枚举对的题数,从n到0跑一遍,预处理出来全部发生的概率pp。然后通过

当 对的题数为n时 :  序列全部连续       得分为     pp * n^m.

当对的题数为n-1时:  我们要判断错的那个题在什么位置   得分为累积和: 令i从1到n,  pp*(100-p[i])/p[i] * [(i-1)^m+(n-i)^m]  

当对的题数为n-2时: 类比得分为   令i从1到n,j从i+1到n, pp*(100-p[i])/p[i]*(100-p[j])/p[j] *[(i-1)^m+(j-1-i)^m+(n-j)^m]

.................

当对的题为0时   得分  也为0。

后来仔细想想这不就是把所有的情况都算了一遍吗,类似于概率论求期望。当n为1000时,时间复杂度为(2^n),这是万万不可以的。看到大家用时不多,所以我又想到这可能是个规律题。又疯狂开始找递推公式,一找就是一下午。

步入正题:

首先我们要把100的逆元处理出来,毕竟题意中给的给的是p[i],而这件事发生的概率为p[i]/100.

然后我们枚举到每个题的位置时它的得分。不断用一层循环遍历这个位置之前的状态。把每一种情况都考虑进去。

代码如下:

#include
#define maxn 1005
#define mod 1000000007
typedef long long  ll;
using namespace std;
ll p[maxn],f[maxn];
ll _pow(ll a,ll b){
    ll ans=1;
  while(b){
    if(b&1)  ans=ans*a%mod;
     a=a*a%mod;
        b=b>>1;
  }
  return ans%mod;
}

int main(){
  int n,m;
    ll inv = _pow(100,mod-2);
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
         scanf("%lld",&p[i]);
    for(int i=1;i<=n;i++)
    {
        f[i]=f[i-1]*(100-p[i])%mod;
        f[i]=f[i]*inv%mod;
        ll pb=1,sum;
        for(int j=1;j<=i;j++)
         {
           sum=j;
           pb=pb*p[i-j+1]%mod;
           pb=pb*inv%mod;
           ll tmp = pb*(100-p[i-j])%mod;
           tmp=tmp*inv%mod;
           ll a;
           if(i==j)  a=0;
           else
              a=f[i-j-1];
           f[i]=(f[i]+tmp*(_pow(sum,m)+a)%mod)%mod;
         }
    }
    printf("%d\n",f[n]);
    return 0;
}

题目

链接:https://www.nowcoder.com/acm/contest/147/E
来源:牛客网
 

 

你可能感兴趣的:(牛客第9场-E 概率dp)