cdqz2017-test10-柚的策略(期望DP & 组合数学)

根据期望的可加性,我们可以算出每一位客人的期望等待时间,将他们累加

即 每一位客人所有可能情况的时间之和 / n!

设S= 每一位客人所有可能情况的时间之和

如果有f(i,p)种方案使客人i是恰好第p个进入花亭的,那对S的贡献为(n-p+1)* t[i] * f(i,p)

所以问题转变为计算f(i,p),客人i是恰好第p个进入花亭的方案数

这个恰好很难算

所以转化为在前p个进入花亭的客人中有i,最后f(i,p)减f(i,p-1)就得到了恰好是第p个

若前k+p-1个客人中至少有k-1个客人的用时比第i个客人的用时多,那么客人i可以在前p个进入花亭

所以问题又转化为了计算dp(i,j,l),在i个客人里至少有j个客人的用时比第l个客人用时多,且第i个客人一定在这i个客人里的方案数

换个状态定义会更好算:

用时相同的客人谁先进谁后进对答案没有影响

将客人的用时映射到1——n

dp(i,j,l),在1——n里选i个数至少有j个数比l大 且 不能选l的方案数

可以得到方程:(把f换成dp)

有了dp(i,j,l),再来算f(i,p)

前面说了若前k+p-1个客人中至少有k-1个客人的用时比第i个客人的用时多,那么客人i可以在前p个进入花亭

假设t[i]映射到了c

所以f(i,p)= dp[k+p-1-1][k-1][c]*(k+p-1)*(n-(k+p-1))!

因为客人i要占据一个位置,这个位置有(k+p-1)种选择,

在客人i之后的客人可以随意组合,所以是全排列

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

#define N 301 
#define M 1000001

const int mod=1e9+7;

int t[N];
int v[M],rk[N];

int C[N][N];
int fac[N];

int dp[N][N][N];
int f[N][N];

void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } 
}

int Pow(int a,int b)
{
    int res=1;
    for(;b;a=1LL*a*a%mod,b>>=1)
        if(b&1) res=1LL*a*res%mod;
    return res;
}

int main()
{
    freopen("strategy.in","r",stdin);
    freopen("strategy.out","w",stdout); 
    int n,k;
    read(n); read(k);
    for(int i=1;i<=n;++i) read(t[i]),v[t[i]]++;
    for(int i=1;i<M;++i) v[i]+=v[i-1];
    for(int i=1;i<=n;++i) rk[i]=v[t[i]]--;
    C[0][0]=1;
    fac[0]=1;
    for(int i=1;i<=n;++i)
    {
        C[i][0]=1;
        fac[i]=1LL*fac[i-1]*i%mod;
        for(int j=1;j<=i;++j) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    }
    for(int i=0;i<=n;++i)
    {
        for(int j=0;j<=i;++j)
            for(int l=1;l<=n;++l)
                dp[i][j][l]=1LL*C[n-l][j]*C[l-1][i-j]%mod*fac[i]%mod; 
        //选i个数恰好有j个数比l大的方案数
        for(int j=i;j>=0;--j) 
            for(int l=1;l<=n;++l)
            {
                dp[i][j][l]+=dp[i][j+1][l]; 
                dp[i][j][l]-=dp[i][j][l]>=mod ? mod : 0;
            }
        //选i个数至少有j个数比l大的方案数
    }
    int c,m,h; 
    int ans=0;
    for(int i=1;i<=n;++i)
    {
        c=rk[i];
        for(int j=1;j<=n;++j) // 第i个人是前j个进入花亭的 
        {
            m=min(k+j-1,n);
            h=k-1-max(0,k+j-1-n);
            f[i][j]=1LL*dp[m-1][h][c]*m%mod*fac[n-m]%mod; 
        }
        for(int j=n;j;--j)
        {
            f[i][j]-=f[i][j-1];
            if(f[i][j]<0) f[i][j]+=mod;
            ans=(ans+1LL*f[i][j]*(n-j+1)%mod*t[i]%mod)%mod;
        //    printf("%d
",ans);
        }
    }
    ans=1LL*ans*Pow(fac[n],mod-2)%mod; 
    printf("%d",ans);
}
原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8719157.html