BZOJ 2655: calc

2655: calc

Time Limit: 30 Sec  Memory Limit: 512 MB
Submit: 182  Solved: 119
[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 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

 

Source

分析:

首先我们可以得出一个时间空间复杂度都爆炸的dp方程,大概可以拿到20分的样子...

首先我们将序列有序化,然后最后乘上$n!$...

定义$f[i][j]$为用$[1,i]$里面的数字组成$a[1]~a[j]$的答案...

那么$f[i][j]=f[i-1][j]+f[i-1][j-1]*i$...

然后我就不会了...跑去膜拜题解...http://blog.csdn.net/ta201314/article/details/52753481

发现有两种做法:拉格朗日插值法&容斥原理...

首先是拉格朗日插值法...

我们可以发现,所有的$f[i][j](i<j)$都是$0$,然后对于所有的$f[i][j](i>=j)$我们研究暴力转移的方程:$f[i][j]=i*\sum _{k=1}^{i-1} f[k][j-1]$,发现这是一个$f[i][j]=i*s(i-1,j-1)$的形式,那么我们猜想$f[x][i]$是一个关于$x$的多项式,然后发现对于$f[x][i]$和$f[x][i-1]$,$f[x][i]$比$f[x][i-1]$的次数大二:一个是前缀和使得次数+1,另一个是乘了一个$x$又使得次数+1...所以其实$f[i][j]$就是一个最高项次数为$2j$的多项式,那么问题就是求出每一项的系数,用拉格朗日插值法来求...

拉格朗日插值法就是$f(x)=\sum _{i=0}^{n} a_{i}x^{i}=\sum _{i=0}^{n} f(x _{i})\prod _{j=0}^{n} \frac {x-x _{j}}{x _{i}-x _{j}}[i≠j]$

有了这个东西我们就可以在$O(2*n*n)$的时间复杂度内得出答案...

代码:

拉格朗日插值法:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
using namespace std;

const int maxn=1000+5,maxm=500+5;

int n,m,cnt,mod;

long long x[maxn],y[maxn],f[maxn+maxm][maxm],fac[maxn];

long long power(long long x,int y){
	long long ans=1;
	while(y){
		if(y&1)
			(ans*=x)%=mod;
		(x*=x)%=mod,y>>=1;
	}
	return ans;
}

signed main(void){
	scanf("%d%d%d",&m,&n,&mod);
	f[0][0]=1;fac[0]=1;cnt=0;
	for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
	for(int i=1;i<=n*3+5;i++)
		for(int j=0;j<=n;j++){
			if(j)
				f[i][j]=(f[i-1][j]+f[i-1][j-1]*i%mod)%mod;
			else
				f[i][j]=f[i-1][j];
		}
	if(n*3+5>=m){
		printf("%lld\n",f[m][n]*fac[n]%mod);
		return 0;
	}
	for(int i=1;cnt<=(n<<1)+1;i++)
		if(f[i][n]&&i!=m)
			x[++cnt]=i,y[cnt]=f[i][n];
	long long a=1,b,ans=0;
	for(int i=1;i<=cnt;i++)
		(a*=(m-x[i]+mod)%mod)%=mod;
	for(int i=1;i<=cnt;i++){
		b=(m-x[i]+mod)%mod;
		for(int j=1;j<=cnt;j++)
			if(i!=j)
				(b*=(x[i]-x[j]+mod)%mod)%=mod;
		(ans+=a*power(b,mod-2)%mod*y[i]%mod)%=mod;
	}
	printf("%lld\n",ans*fac[n]%mod);
	return 0;
}

  


By NeighThorn

posted @ 2017-02-20 21:54  NeighThorn  阅读(367)  评论(0编辑  收藏  举报