牛客多校round9 E Music Game

题目:https://www.nowcoder.com/acm/contest/147/E

Niuniu likes to play OSU!
We simplify the game OSU to the following problem.

Given n and m, there are n clicks. Each click may success or fail.
For a continuous success sequence with length X, the player can score X^m.
The probability that the i-th click success is p[i]/100.
We want to know the expectation of score.
As the result might be very large (and not integral), you only need to output the result mod 1000000007.
输入描述:
The first line contains two integers, which are n and m.
The second line contains n integers. The i-th integer is p[i].

1 <= n <= 1000
1 <= m <= 1000
0 <= p[i] <= 100
输出描述:
You should output an integer, which is the answer.

sol:

这个题有 O ( n 2 ) 的做法,这里不多加讨论。说下从bls学到的用第二类 S t i r l i n g 数优化计算 x m 的方法。

1.考虑朴素的dp

x 为最长连续成功后缀的长度。设 a [ i ] [ j ] 为考虑前i次点击, x j 的期望。
由二项式定理可知

( x + 1 ) m = i = 0 m ( m i ) x i

从而得到转移方程
a [ i ] [ j ] = p [ i ] k = 0 j ( j k ) a [ i 1 ] [ k ]

b [ i ] [ j ] x j 的得分期望,由
( x + 1 ) m x m = i = 0 m 1 ( m i ) x i

则有

b [ i ] [ j ] = { b [ i 1 ] [ j ] ( f a i l ) b [ i 1 ] [ j ] + k = 0 m 1 ( m k ) a [ i 1 ] [ k ] ( s u c e e s s )

合并起来就是

b [ i ] [ j ] = b [ i 1 ] [ j ] + p [ i ] k = 0 m 1 ( m k ) a [ i 1 ] [ k ]

这样做的复杂度是 O ( n m 2 )

2.用Stirling数优化

先放一个结论

n p = k = 0 p S ( p , k ) [ n ] k

也可以写为
n p = k = 0 p S ( p , k ) i ! ( n k )

这个可以在组合数学的教材里找到。

[ n ] k 表示n的下阶乘,等价于 A ( n , k ) S ( p , k ) 表示第二类 s t i r l i n g

那么我们可以将维护 n p 的期望 转换到维护 ( n k ) 的期望上去。

同样的,我们设最长成功后缀的长度为 x ,设 a [ i ] [ j ] 为 考虑到第i 位后 ( x j ) 的期望,设 b [ i ] [ j ] 为所有 ( x j ) 的期望。

由杨辉三角:

( n i ) = ( n 1 i ) + ( n 1 i 1 )

可得

a [ i ] [ j ] = p [ i ] ( a [ i 1 ] [ j ] + a [ i 1 ] [ j 1 ] )

同理可得

b [ i ] [ j ] = b [ i 1 ] [ j ] + p [ i ] ( a [ i 1 ] [ j 1 ] )

至此,这道题就可以能够在 O ( n m ) 的时间内完成.

code:

#include <bits/stdc++.h>
using namespace std;
int n, m, mod = 1000000007;
long long p[1020];
long long a[1020][1020];
long long b[1020][1020];
long long s[1020][1020];
long long f[1020];

typedef long long ll;

int main() {
    scanf("%d%d", &n, &m);
    s[0][0] = 1;
    for (int i = 0; i <= m; i++) {
        for (int j = 1; j <= i; j++) {
            s[i][j] = (s[i - 1][j - 1] + j * s[i - 1][j]) % mod;
        }
    }
    for (int i = f[0] = 1; i <= m; i++) {
        f[i] = f[i - 1] * i % mod;
    }
    a[0][0] = 1;
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &p[i]);
        p[i] = p[i] * 570000004 % mod;
        a[i][0] = 1;
        for (int j = 1; j <= m; j++) {
            a[i][j] = a[i-1][j] + a[i-1][j - 1];
            a[i][j] *= p[i];
            a[i][j] %= mod;
            b[i][j] = p[i] * a[i-1][j-1]; 
            b[i][j] %= mod;
            b[i][j] += b[i-1][j];
            b[i][j] %= mod;
        }
    }
    long long z = 0;
    for (int i = 1; i <= m; i++) {
        z = (z + b[n][i] * f[i] % mod * s[m][i]) % mod;
    }
    printf("%lld\n", z);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/oWuHen12/article/details/81807864