把概率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<bits/stdc++.h>
#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
来源:牛客网