牛客2018暑假多校训练九-E(期望)

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

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

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.

示例1

输入

复制

3 4
50 50 50

输出

复制

750000020

说明

000 0
001 1
010 1
011 16
100 1
101 2
110 16
111 81

The exact answer is (0 + 1 + 1 + 16 + 1 + 2 + 16 + 81) / 8 = 59/4.
As 750000020 * 4 mod 1000000007 = 59
You should output 750000020.

备注:

If you don't know how to output a fraction mod 1000000007,
You may have a look at https://en.wikipedia.org/wiki/Modular_multiplicative_inverse

题意:有一个长度为n只包含01的序列,每个位置为1的可能性给出,每段连续的长度为x的序列可以得到分数x^m,问最后得到总分期望。

思路:

BZOJ 3450 平方的版本。

BZOJ 4318 立方的版。

hdu 4625 JZPTREE,组合数维护起来比较简单,最后转回次方即可。

对于m等于2的情况 (x+1)^2 = x^2+2x+1 .每次只需要在前面平方期望的基础上加上(前面长度期望的两倍加上1)*现在这个点为1的概率。

m=3同理。

但是m<=1000不好维护

在hdu4625中给出了把幂转换为组合数来维护的方法,再通过第二类Stirling数变回来。C(n,k) = C(n-1,k) + C(n-1,k-1)比较好转移。

 

hdu4625题目链接

hdu4625代码

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn =5e4+10;
const int maxk = 505;
const int mod = 10007;
int n,k;
vector<int>g[maxn];
int dp[maxn][maxk];
int fac[maxk];
int s[maxk][maxk];  
void init()
{
    fac[0] = 1;
    for(int i = 1;i<maxk;i++)
        fac[i] = (fac[i-1] * i)%mod;
    memset(s,0,sizeof(s));
    s[1][1]=1;  
    for(int i=2;i<maxk;i++)  
        for(int j=1;j<=i;j++)  
    	{  
        	s[i][j]=(s[i-1][j-1]+(j*s[i-1][j])%mod)%mod;  
        }
}
void dfs(int u,int f)
{
    int v;
    for(int i = 0;i<g[u].size();i++)
    {
        v = g[u][i];
        if(v==f)continue;
        dfs(v,u);
        dp[u][0] = (dp[u][0] + dp[v][0])%mod;
        for(int j = 1;j<=k;j++)
        {
            dp[u][j] = ((dp[u][j] + dp[v][j])%mod + dp[v][j-1])%mod;
        }
    }
    dp[u][0]++;
    dp[u][0] %= mod;
}
void dfs2(int u,int f)
{
    int v;
    for(int i = 0;i<g[u].size();i++)
    {
        v = g[u][i];
        if(v==f)continue;
        for(int j = k;j>0;j--)
        {
            int p = ((dp[u][j] - dp[v][j] + mod)%mod - dp[v][j-1] +mod)%mod;
            int q = ((dp[u][j-1] - dp[v][j-1] + mod)%mod - (j-2>=0?dp[v][j-2]:0) + mod)%mod;
            dp[v][j] = (dp[v][j] + p + q)%mod;
        }
        dp[v][0] = (dp[v][0] + (dp[u][0] - dp[v][0] + mod)%mod)%mod;
        dfs2(v,u);
    }
}
int main()
{
    init();
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(dp,0,sizeof(dp));
        scanf("%d%d",&n,&k);
        for(int i = 0;i<=n;i++)
        {
            g[i].clear();
        }
        int u,v;
        for(int i=0;i<n-1;i++)
        {
            scanf("%d%d",&u,&v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        dfs(1,-1);
        dfs2(1,-1);
        int ans;    
        for(int u = 1;u<=n;u++)
        {
            ans = 0;
            for(int i = 0;i<=k;i++)
            {
                ans = (ans + ((s[k][i] * fac[i])%mod * dp[u][i])%mod)%mod;
            }
            printf("%d\n",ans);
        }
    }
}

这一题中也可以这样做,标程代码中组合数的维护方法学习了。

#include <bits/stdc++.h>
using namespace std;
int n, m, mod = 1000000007;
long long p[1020];
long long a[1020];
long long b[1020];
long long s[1020][1020];
long long f[1020];
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[i]为以i结尾的一段连续combo的组合数期望,b[i]为前面总的组合数期望
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &p[i]);
        p[i] = p[i] * 570000004 % mod;
        for (int j = m; j > 0; j--) {
            a[j] += a[j - 1];
            a[j] *= p[i];
            a[j] %= mod;
            b[j] += a[j];
            b[j] %= mod;
        }
        a[0] = p[i];
        b[0] += a[0];
        b[0] %= mod;
    }
    long long z = 0;
    for (int i = 1; i <= m; i++) {
        z = (z + b[i - 1] * f[i] % mod * s[m][i]) % mod;
    }
    printf("%lld\n", z);
    return 0;
}

这一题还可以用更简单的方法去做,枚举每一个区间为完整的一个combo得到的值乘以概率全部加到ans上即可。

猜你喜欢

转载自blog.csdn.net/qq_25576697/article/details/81783492