[bzoj4559][JLoi2016]成绩比较

来自FallDream的博客,未经允许,请勿转载,谢谢。


G系共有n位同学,M门必修课。这N位同学的编号为0到N-1的整数,其中B神的编号为0号。这M门必修课编号为0到M-1的整数。一位同学在必修课上可以获得的分数是1到Ui中的一个整数。如果在每门课上A获得的成绩均小于等于B获得的成绩,则称A被B碾压。在B神的说法中,G系共有K位同学被他碾压(不包括他自己),而其他N-K-1位同学则没
有被他碾压。D神查到了B神每门必修课的排名。这里的排名是指:如果B神某门课的排名为R,则表示有且仅有R-1位同学这门课的分数大于B神的分数,有且仅有N-R位同学这门课的分数小于等于B神(不包括他自己)。我们需要求出全系所有同学每门必修课得分的情况数,使其既能满足B神的说法,也能符合D神查到的排名。这里两种情况不同当且仅当有任意一位同学在任意一门课上获得的分数不同。你不需要像D神那么厉害,你只需要计算出情况数模10^9+7的余数就可以了。
n,m<=100  U<=10^9
 
先不考虑成绩是多少,只考虑大小关系,容易得出一个dp
f[i][j]表示前i门课,恰好碾压了j个人的方案数,转移时候枚举现在这门课有多少人先前被碾压,现在还被碾压,可以用组合数转移一下。
然后考虑成绩大小,也就是求一个式子
$sum_{i=1}^{U}i^{n-Ri}(U-i)^{Ri-1}$
容易发现这是一个n+1次的多项式,插值就行了
顺便400题mark一下。
#include<iostream>
#include<cstdio>
#include<cstring>
#define MN 105
#define mod 1000000007
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}
int inv[MN+5],Inv[MN+5],p[MN+5],U[MN+5],R[MN+5],n,m,K,f[MN+5][MN+5];

inline int pow(int x,int k)
{
    int sum=1;
    for(;k;k>>=1,x=1LL*x*x%mod)
        if(k&1) sum=1LL*sum*x%mod;
    return sum;
}
inline int C(int n,int m){return 1LL*p[n]*Inv[m]%mod*Inv[n-m]%mod;}
inline void Re(int&x,int y){x+=y;x>=mod?x-=mod:0;}

inline int Calc(int m,int rk)
{
    if(m<=n+1)
    {
        int res=0;
        for(int i=1;i<=m;++i)
            res=(res+1LL*pow(i,rk)*pow(m-i,n-rk-1))%mod;
        return res;
    }
    int sum=1,res=0,Div=1,S=0;
    for(int i=1;i<=n+1;++i) sum=1LL*sum*(m-i+mod)%mod;
    for(int i=2;i<=n+1;++i) Div=1LL*Div*(1-i+mod)%mod;
    for(int i=1;i<=n+1;++i)
    {
        int t=1LL*sum*pow(m-i+mod,mod-2)%mod;
        S=(S+1LL*pow(i,rk)%mod*pow(m-i,n-rk-1))%mod;
        t=1LL*t*S%mod;
        res=(res+1LL*t*pow(Div,mod-2))%mod;
        Div=1LL*Div*pow(mod-(n-i+1),mod-2)%mod*i%mod;
    }
    return res;
}

int main()
{
    n=read();m=read();K=read();
    for(int i=1;i<=m;++i) U[i]=read();
    for(int i=1;i<=m;++i) R[i]=n-read();
    inv[0]=inv[1]=p[0]=p[1]=Inv[0]=1;
    for(int i=2;i<=MN;++i)
        p[i]=1LL*p[i-1]*i%mod,
        inv[i]=1LL*(mod-mod/i)*inv[mod%i]%mod;
    for(int i=1;i<=MN;++i)
        Inv[i]=1LL*Inv[i-1]*inv[i]%mod;
    f[0][n-1]=1;
    for(int i=1;i<=m;++i)
        for(int j=0;j<=n-1;++j)
            for(int k=0;k<=min(j,R[i]);++k) if(n-1-j>=R[i]-k)
                Re(f[i][k],1LL*f[i-1][j]*C(j,k)%mod*C(n-1-j,R[i]-k)%mod);
    int ans=f[m][K];
    for(int i=1;i<=m;++i) ans=1LL*ans*Calc(U[i],R[i])%mod;
    cout<<ans<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/FallDream/p/bzoj4559.html