[BZOJ2655]calc

Posted by Dispwnl on February 28, 2019

题目

Description

一个序列a1,…,an是合法的,当且仅当:

长度为给定的n。

a1,…,an都是[1,A]中的整数。

a1,…,an互不相等。

一个序列的值定义为它里面所有数的乘积,即a1a2…an。

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

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

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

Input

一行3个数,A,n,mod。意义为上面所说的。

Output

一行结果

Sample Input

9 7 10007

Sample Output

3611

HINT

数据规模和约定

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

题解

设$f_{i,j}$表示填到$i$,最大值不超过$j$的答案,有转移$f_{i,j}=ij\times f_{i-1,j-1}+f_{i,j-1}$

发现$A\le 10^9$,而这个玩意我感觉肯定不能用矩乘啥的优化

通过打表(并不)可以发现这个玩意是个最高次为$2n$的多项式

然后就可以用拉格朗日插值优化了

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<algorithm>
using namespace std;
const int N=1e3+5;
int n,A,mod,_N,ans;
int w[N];
int f[N][N];
int Pow(int a,int b)
{
	int ans=1;
	for(;b;b>>=1,a=1ll*a*a%mod)
	  if(b&1) ans=1ll*ans*a%mod;
	return ans;
}
int L(int x)
{
	int ans=1;
	for(int i=0;i<=_N;++i)
	 if(x!=i) ans=(1ll*ans*(A-i)%mod*(x-i<0?-1:1)*w[x-i<0?i-x:x-i]%mod+mod)%mod;
	return ans;
}
int main()
{
	scanf("%d%d%d",&A,&n,&mod),_N=n<<1;
	for(int i=1;i<=_N;++i)
	  {
		f[1][i]=f[1][i-1]+i;
		if(f[1][i]>=mod) f[1][i]-=mod;
	  }
	for(int i=1;i<=_N;++i)
	  for(int j=2;j<=n;++j)
		{
			f[j][i]=1ll*f[j-1][i-1]*i%mod*j%mod+f[j][i-1];
			if(f[j][i]>=mod) f[j][i]-=mod;
		}
	if(A<=_N) return printf("%d",f[n][A]),0;
	for(int i=1;i<=_N;++i)
	  w[i]=Pow(i,mod-2);
	for(int i=0;i<=_N;++i)
	  {
		ans+=1ll*f[n][i]*L(i)%mod;
		if(ans>=mod) ans-=mod;
	  }
	return printf("%d",ans),0;
}