BZOJ2655 calc(动态规划+拉格朗日插值法)

  考虑暴力dp:f[i][j]表示i个数值域1~j时的答案。考虑使其值域++,则有f[i][j]=f[i][j-1]+f[i-1][j-1]*i*j,边界f[i][i]=i!*i!。

  注意到值域很大,考虑能不能在这一维上优化。完全不会证地有f[i][j]是一个关于j的2i次多项式。那么dp出一部分后就可以直接拉格朗日插值求出多项式,代入即可。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
#define N 510
int n,m,P,f[N][N<<1],fac[N],ans=0;
int ksm(int a,int k)
{
    if (k==0) return 1;
    int tmp=ksm(a,k>>1);
    if (k&1) return 1ll*tmp*tmp%P*a%P;
    else return 1ll*tmp*tmp%P;
}
int inv(int x){return ksm(x,P-2);}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj2655.in","r",stdin);
    freopen("bzoj2655.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    m=read(),n=read(),P=read();
    fac[0]=1;for (int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%P;
    for (int i=0;i<=n;i++)
    {
        f[i][i]=1ll*fac[i]*fac[i]%P;
        for (int j=i+1;j<=(n<<1);j++)
        f[i][j]=(f[i][j-1]+1ll*f[i-1][j-1]*i%P*j%P)%P;
    }
    for (int i=0;i<=(n<<1);i++)
    {
        int w=f[n][i],v=1;
        for (int j=0;j<=(n<<1);j++)
        if (i!=j) w=(1ll*w*(m-j+P)%P)%P,v=1ll*v*(i-j+P)%P;
        ans=(ans+1ll*w*inv(v))%P;
    }
    cout<<ans;
    return 0;
}

 

posted @ 2018-09-05 22:34  Gloid  阅读(310)  评论(0编辑  收藏  举报