BZOJ4599[JLoi2016&LNoi2016]成绩比较(dp+拉格朗日插值)

Posted 大奕哥&VANE

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ4599[JLoi2016&LNoi2016]成绩比较(dp+拉格朗日插值)相关的知识,希望对你有一定的参考价值。

这个题我们首先可以dp,f[i][j]表示前i个科目恰好碾压了j个人的方案数,然后进行转移。我们先不考虑每个人的分数,先只关心和B的相对大小关系。我们设R[i]为第i科比B分数少的人数,则有f[i][j]=sum f[i-1][k]*C(k,j)*C(n-1-k,R[i]-j)  (k>=j) 怎么解释呢,首先前i-1科有k个人已经被碾压,k肯定大于等于j,然后考虑当前这一科有j个人被碾压,那么就需要从k个人中选出j个来即C(k,j),然后从剩下的有R[i]-j个人比B考的少,这些人必须是之前i-1科里就没有被碾压的人,所以再乘上一个C(n-1-j,R[i]-k),到此我们dp完了,可是我们还需要算上每个人的分数,这个东西很明显可以乘上我们的f[m][k]得到答案。 这些分数的方案数是什么呢?对于第i科成绩,有n-R[i]-1个人比B考的多,有R[i]个人比B少,因为我们之前考虑了相对大小关系,这里直接很明显的算就行了就是

然后我们算n次sigam即可,把他们乘在一起。但是由于Ui是1e9级别的,直接暴力算肯定会超时,我们可以用拉格朗日插值来算。

很明显这是一个n次的多项式,所以我们利用插值就可以算出答案了。 —— by VANE

#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;
    R[0]=n-1;
    for(int i=1;i<=m;++i)
        for(int j=0;j<=R[i];++j)
            for(int k=j;k<=R[i-1];++k)
            if(n-1-k>=R[i]-j)
            Re(f[i][j],1ll*f[i-1][k]*C(k,j)%mod*C(n-1-k,R[i]-j)%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;
}

 

以上是关于BZOJ4599[JLoi2016&LNoi2016]成绩比较(dp+拉格朗日插值)的主要内容,如果未能解决你的问题,请参考以下文章

bzoj4559JLOI2016成绩比较

bzoj4558[JLoi2016]方 容斥+count

bzoj4561: [JLoi2016]圆的异或并 圆的扫描线

bzoj 4559 [JLoi2016]成绩比较——拉格朗日插值

BZOJ4561[JLoi2016]圆的异或并 扫描线

bzoj千题计划270:bzoj4559: [JLoi2016]成绩比较