「BZOJ2655 calc」 - 拉格朗日插值

BZOJ2655 calc

题意

一个序列 \(a_1,\cdots,a_n\) 是合法的,当且仅当:

  • 长度为给定的 \(n\)
  • \(a_1,\cdots,a_n\) 都是 \([1,A]\) 中的整数
  • \(a_1,\cdots,a_n\) 互不相等

一个序列的值定义为它里面所有数的乘积, 即 \(\Pi_{i=1}^n a_i\)

求所有不同合法序列的值的和

两个序列不同当且仅当他们任意一位不一样

输出答案对一个数 mod 取余的结果

题解

显然求的是

\[ [x^n]\Pi_{i=1}^A(ix+1) \]

由于 \(A\) 很大,不能直接求,于是令 \(f_{i,j}\) 表示 \([x^j]\Pi_{k=1}^i(kx+1)\)

易得

\[ f_{i,j}=f_{i-1,j-1}\times j+f_{i-1,j} \]

然后展开 \(f_{i-1,j}\), 一直展开

得到

\[ f_{i,j}=\sum_{k=0}^{i-1}(k+1)f_{k,j-1} \]

这说明 \(f_{i,j}\) 可以表示为一个多项式

其中 \(f_{i,j}\) 的次数是 \(f_{i,j-1}\) 的次数加 2,因为一个是前缀和的次数,一个是乘系数的次数

然后拉格朗日插值算第 \(A\) 项就好了

#include<cstdio>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
template<typename T>inline void rd(T&x){int fl=0,ch;while(ch=getchar(),ch<48||57<ch)fl^=!(ch^45);x=(ch&15);while(ch=getchar(),47<ch&&ch<58)x=x*10+(ch&15);if(fl)x=-x;}
template<typename T>inline void pt(T x){if(x<0)putchar('-'),x=-x;if(x>9)pt(x/10);putchar(x%10+48);}
template<typename T>inline void pt(T x,int ch){pt(x),putchar(ch);}
template<typename T>inline T max(const T&x,const T&y){return x<y?y:x;}
template<typename T>inline T min(const T&x,const T&y){return x<y?x:y;}
const int N=2005;
int A,n,P,f[N][N],inv[N];
int main(){
    rd(A),rd(n),rd(P);
    inv[0]=inv[1]=1;
    for(int i=2;i<N;++i)inv[i]=1ll*(P-P/i)*inv[P%i]%P;
    f[1][0]=f[1][1]=1;
    for(int i=2;i<=n*2+1;++i){
        f[i][0]=1;
        for(int j=1;j<=n;++j)
            f[i][j]=(1ll*i*f[i-1][j-1]%P+f[i-1][j])%P;
    }
    int ans=0;
    for(int i=1;i<=n*2+1;++i){
        int x=f[i][n];
        for(int j=1;j<=n*2+1;++j)if(i^j)x=1ll*x*((A-j+P)%P)%P,x=1ll*x*(i>j?inv[i-j]:P-inv[j-i])%P;
        (ans+=x)%=P;
    }
    for(int i=1;i<=n;++i)ans=1ll*ans*i%P;
    printf("%d\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/xay5421/p/BZOJ2655.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值