nexthexonextbutterflyvolantisyearnyiliashokaindigoapollolandscapecactusmateryicarusfluidmaterial
转移自老blog

bzoj2655

此文更新于2019.6.5
一个序列a1,...,an是合法的,当且仅当:
长度为给定的n。
a1,...,an都是[1,A]中的整数。
a1,...,an互不相等。
一个序列的值定义为它里面所有数的乘积,即a1a2...an。
求所有不同合法序列的值的和。
两个序列不同当且仅当他们任意一位不一样。
输出答案对一个数mod取余的结果。
f(i,j)-> 前i个元素中最大值为j的方案的权的和
f(i,j)=f(i-1,j-1)*i*j+f(i,j-1)
用数学归纳法证明f(i,j)关于j是一个最高次为2*i的多项式
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
ll mod;

ll qpow(ll a,ll b,ll mod){
    ll ret=1;
    while(b){
        if(b&1ret=ret*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ret;
}

// (mod%i)=== -mod/i*i
const int maxn=1200;
ll fac_inv[maxn]={1,1};
void inv_ini(){
    for(ll i=0,fac=1;i<maxn;i++,fac=fac*i%mod) {
        fac_inv[i]=qpow(fac,mod-2,mod);
    }
}

ll prepre[maxn],suf[maxn],*pre=prepre+1;
ll getval(ll *y,ll n,ll x){// O(n) n次多项式有n+1项 y[0]...y[n] -> y[x]
    pre[-1]=suf[n+1]=1;
    for(int i=0;i<=n;i++pre[i]=pre[i-1]*(x-i+mod)%mod;
    for(int i=n;i>=0;i--suf[i]=suf[i+1]*(i-x+mod)%mod;
    ll ret=0;
    for(int i=0;i<=n;i++) {
        ll up=pre[i-1]*suf[i+1]%mod;
        ll down=fac_inv[i]*fac_inv[n-i]%mod;
        ret=(ret+y[i]*up%mod*down)%mod;
    }
    return ret;
}

// f(i,j)-> 前i个元素中最大值为j的方案的权的和
// f(i,j)=f(i-1,j-1)*i*j+f(i,j-1)
// 用数学归纳法证明f(i,j)关于j是一个最高次为2*i的多项式
ll f[maxn][maxn*3];
int main(){
    ll n,a;
    while(~scanf("%lld%lld%lld",&a,&n,&mod)){
        inv_ini();
        for(ll j=1;j<=3*n;j++f[1][j]=(f[1][j-1]+j)%mod;
        for(ll i=2;i<=n;i++){
            for(ll j=i;j<=3*n;j++){
                f[i][j]=(i*j*f[i-1][j-1]+f[i][j-1])%mod;
            }
        }
        //we know f(n) f(n+1) ... f(3n)
        //if g(i)=f(i+n)
        // than f(a)=g(a-n)
        printf("%lld\n",getval(f[n]+n,2*n,(a-n+mod)%mod));
    }
}
此文标签
拉格朗日插值法