2655: calc
Time Limit: 30 Sec Memory Limit: 512 MB
Submit: 428 Solved: 246
[Submit][Status][Discuss]Description
一个序列a1,...,an是合法的,当且仅当:
长度为给定的n。
a1,...,an都是[1,A]中的整数。
a1,...,an互不相等。
一个序列的值定义为它里面所有数的乘积,即a1a2...an。
求所有不同合法序列的值的和。
两个序列不同当且仅当他们任意一位不一样。
输出答案对一个数mod取余的结果。Input
一行3个数,A,n,mod。意义为上面所说的。
Output
一行结果。
Sample Input
9 7 10007Sample Output
3611HINT
数据规模和约定
0:A<=10,n<=10。
1..3:A<=1000,n<=20.
4..9:A<=10^9,n<=20
10..19:A<=10^9,n<=500。
全部:mod<=10^9,并且mod为素数,mod>A>n+1
Source
https://blog.****.net/qq_20669971/article/details/52790835
先列出DP方程,f[i][j]表示i个元素选j个进排列的总贡献,则f[i][j]=f[i-1][j-1]*i*j+f[i-1][j]。(这里也可以把n!提出来)
直接DP肯定不行,然后我们可以发现f[i][j]实际上是关于i的高次多项式,现在要确定是几次多项式。
第一种方法:f[i][i]=(i!)^2,根据递推式求得f[i][j]是2j次的。
https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8510924.html
第二种方法:打表发现系数中i的次数和i本身的次数都为j。
https://blog.****.net/ez_yww/article/details/77221338
所以确定是2j次的(当然如果不想确定就多放几次也没关系)
这样我们可以用拉格朗日插值法先求出2n个点的f[x_i][n],拟合出多项式后将A代入即可(直接现场代入就好)
#include<cstdio>
#include<algorithm>
#define rep(i,l,r) for (int i=l; i<=r; i++)
typedef long long ll;
using namespace std; const int N=;
int f[N<<][N],x,n,mod,ans,m; int inv(int a){
int res=,b=mod-;
for (; b; a=1ll*a*a%mod,b>>=)
if (b & ) res=1ll*res*a%mod;
return res;
} int main(){
freopen("bzoj2655.in","r",stdin);
freopen("bzoj2655.out","w",stdout);
scanf("%d%d%d",&x,&n,&mod); f[][]=; m=*n+;
rep(i,,m) { f[i][]=; rep(j,,n) f[i][j]=(f[i-][j]+1ll*i*f[i-][j-])%mod; }
rep(i,,m){
int a=f[i][n],b=;
rep(j,,m) if (i!=j) a=1ll*a*(x-j)%mod,b=1ll*b*(i-j)%mod;
a=1ll*a*inv(b)%mod; ans=(ans+a)%mod;
}
rep(i,,n) ans=1ll*ans*i%mod;
printf("%d\n",(ans+mod)%mod);
return ;
}
可以O(m)插值,预处理一些东西就好(不过瓶颈在于DP所以无所谓)
边界考虑清楚。
#include<cstdio>
#include<algorithm>
#define rep(i,l,r) for (int i=l; i<=r; i++)
typedef long long ll;
using namespace std; const int N=;
int x,n,mod,ans,m,f[N<<][N],pre[N<<],suf[N<<],fac[N<<],fin[N<<]; int inv(int a){
int res=,b=mod-;
for (; b; a=1ll*a*a%mod,b>>=)
if (b & ) res=1ll*res*a%mod;
return res;
} int main(){
freopen("bzoj2655.in","r",stdin);
freopen("bzoj2655.out","w",stdout);
scanf("%d%d%d",&x,&n,&mod); m=*n+;
rep(i,,m) f[i][]=;
rep(i,,m) rep(j,,n) f[i][j]=(f[i-][j]+1ll*i*f[i-][j-])%mod;
pre[]=; rep(i,,m) pre[i]=1ll*pre[i-]*(x-i)%mod;//(x-1)*(x-2)*...*(x-i)
suf[]=(x-m)%mod; rep(i,,m) suf[i]=1ll*suf[i-]*(x-m+i)%mod;//(x-m)*(x-m+1)*...(x-m+i)
fac[]=; rep(i,,m) fac[i]=1ll*fac[i-]*i%mod;//1*2*...*i
fin[m]=inv(fac[m]); for (int i=m-; ~i; i--) fin[i]=1ll*fin[i+]*(i+)%mod;//1/(1*2*...*i)
rep(i,,m){
int a=1ll*f[i][n]*pre[i-]%mod*((i==m)?:suf[m-i-])%mod;
int b=1ll*fin[i-]%mod*fin[m-i]*(((m-i)&)?-:)%mod;
ans=(ans+1ll*a*b)%mod;
}
rep(i,,n) ans=1ll*ans*i%mod;
printf("%d\n",(ans+mod)%mod);
return ;
}
以及另一道用拉格朗日插值法简化的题目:[BZOJ4559][JLOI2016]成绩比较