bzoj4559 成绩比较【动态规划+拉格朗日插值法】

来源:互联网 发布:the weeknd 知乎 编辑:程序博客网 时间:2024/05/16 15:28

解题思路:

我们设f[i][j]表示前i门课,共有j个人没有被碾压的方案数,那么答案即为f[m][n1k]

假设从f[i1][w]f[i][j](jw)
没被碾压的人数本应增加Ri1人,但其中有一部分可能之前就没被碾压了。
则有t1=jw个人是新增的人数,要从n1w个人中选,方案数为Ct1n1w
还有t2=Ri1t1个人是本就没被碾压的,要从w个人中选,方案数为Ct2w
例如w=100,Ri1=5,j=102,t1=2,t2=3

考虑第i门课的得分方案,若B神得了x分,则有Ri1个人分数大于他,nRi个人分数小等于他,所以总方案数为
g(i)=x=1Ui(Uix)r1xnr
由于Ui很大,无法直接算,但注意到它是一个关于Ui的次数小等于n次的多项式,所以可以用拉格朗日插值法插U=1,2n+1得到n+1个点进而算出g(i)的值。
由于Ri不同,所以对于每门课都要求一遍。

至此得到总的dp方程:

f[i][j]=g(i)w=0jCt1n1wCt2wf[i1][w]

时间复杂度为O(n3)

#include<iostream>#include<cstdio>#include<cstring>#include<string>#include<algorithm>#include<cmath>#include<ctime>#include<queue>#include<vector>#include<set>#include<map>#define ll long longusing namespace std;int getint(){    int i=0,f=1;char c;    for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());    if(c=='-')c=getchar(),f=-1;    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';    return i*f;}const int N=105,p=1e9+7;int n,m,k;int U[N],R[N],c[N][N],f[N][N];int g[N],inv[N];int ksm(int x,int y){    x%=p;int res=1;    for(;y;y>>=1,x=1ll*x*x%p)        if(y&1)res=1ll*res*x%p;    return res;}int Inter(int u,int r){    memset(g,0,sizeof(g));    for(int x=1;x<=n+1;x++)    {        for(int i=1;i<=x;i++)            g[x]=(g[x]+1ll*ksm(x-i,r-1)*ksm(i,n-r)%p)%p;        if(u==x)return g[x];    }    for(int i=1;i<=n+1;i++)    {        inv[i]=1;        for(int j=1;j<=n+1;j++)            if(i!=j)inv[i]=(1ll*inv[i]*(i-j)%p+p)%p;        inv[i]=ksm(inv[i],p-2);    }    int res=0;    for(int i=1;i<=n+1;i++)    {        int tmp=1ll*inv[i]*g[i]%p;        for(int j=1;j<=n+1;j++)            if(i!=j)tmp=1ll*tmp*(u-j)%p;        res=(res+tmp)%p;    }    return res;}int C(int i,int j){    if(i<0||j<0||j>i)return 0;    return c[i][j];}int main(){    //freopen("lx.in","r",stdin);    //freopen("lx.out","w",stdout);    n=getint(),m=getint(),k=getint();    for(int i=1;i<=m;i++)U[i]=getint();    for(int i=1;i<=m;i++)R[i]=getint();    c[0][0]=1;    for(int i=1;i<=n;i++)    {        c[i][0]=1;        for(int j=1;j<=n;j++)            c[i][j]=(c[i-1][j]+c[i-1][j-1])%p;    }    f[0][0]=1;    for(int i=1;i<=m;i++)    {        int tmp=Inter(U[i],R[i]);        for(int j=0;j<=n;j++)        {            for(int w=0;w<=j;w++)                f[i][j]=(f[i][j]+1ll*f[i-1][w]*C(n-1-w,j-w)%p*C(w,R[i]-1-(j-w))%p*tmp%p)%p;        }    }    cout<<f[m][n-1-k]<<'\n';    return 0;}
原创粉丝点击