BZOJ2655calc
题目描述
一个序列a1,...,an是合法的,当且仅当:
长度为给定的n。
a1,...,an都是[1,A]中的整数。
a1,...,an互不相等。
一个序列的值定义为它里面所有数的乘积,即a1a2...an。
求所有不同合法序列的值的和。
两个序列不同当且仅当他们任意一位不一样。
输出答案对一个数mod取余的结果。
题解
先考虑dp。
我们设dp[i][j]表示前i个元素,已经填完了1~j的所有数字,它们的价值和是多少。
转移:dp[i][j]=dp[i][j-1]+dp[i-1][j-1]*j。
第一维比较小,但第二维比较大,所以需要优化。
然后考虑dp[n]是一个x次多项式。
观察转移的形式,dp[i][j]-dp[i][j-1]=dp[i-1][j-1]*j。
前面的是一个差分的形式,多项式的次数会-1。
G[n]-1=G[n-1]+1
又因为G[0]=1,所以x=2n。
所以可以先求出2*n+1项,再上拉格朗日插值。
代码
#include<iostream> #include<cstdio> #define N 1009 using namespace std; typedef long long ll; int n,m,q; ll mod,a[N],f[N][N],A; inline ll rd(){ ll x=0;char c=getchar();bool f=0; while(!isdigit(c)){if(c=='-')f=1;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} return f?-x:x; } inline ll power(ll x,ll y){ ll ans=1; while(y){ if(y&1)ans=ans*x%mod;x=x*x%mod;y>>=1; } return ans; } inline ll ni(ll x){return power(x,mod-2);} inline ll work(int x){ if(x<=m)return a[x]; ll ans=0; for(int i=1;i<=m;++i){ ll res=a[i]; for(int j=1;j<=m;++j){ if(j!=i)res=res*(x-j)%mod*ni(i-j)%mod; } res=(res+mod)%mod; ans=(ans+res)%mod; } return ans; } int main(){ A=rd();n=rd();mod=rd(); m=n*2+1; for(int i=0;i<=m;++i)f[0][i]=1; for(int i=1;i<=n;++i) for(int j=0;j<=m;++j)f[i][j]=(f[i-1][j-1]*j%mod+f[i][j-1])%mod; for(int i=1;i<=m;++i)a[i]=f[n][i]; ll ans=1; for(int i=1;i<=n;++i)ans=ans*i%mod; printf("%lld",ans*work(A)%mod); return 0; }