【BZOJ2655: calc】DP转多项式考虑+拉格朗日插值

。。。。。。  

2655: calc

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

  f[i][j]即是在前i个数字中选取了j个数字加入进序列中的总方案乘积的总和。那么有f[i][j] = f[i-1][j] + f[i-1][j-1]*i*j (即不加入数字和加入数字i,然后考虑放到哪个位置),最后答案f[A][n] 如果我们把起看做关于i的多项式考虑,写几项出来发现f[i][0] = 1 , f[ i ][ 1 ] = 1 / 2 * i ^ 2 - i , f[ i ][ 2 ] = 1 / 4 * i ^ 4 + 1/6*i^3 -1 / 4 * i ^ 2 - 1 / 6 * i 我们写几项出来,利用数学归纳法,发现f[i][n],以i为自变量,是一个最高次次数为2*n的多项式。 那么我们暴力dp出2*n+1个f[i][n]之后,利用拉格朗日插值代入进去就可以了。 [latex] {F(x)=\sum_{i=1}^n{\frac{\prod_{{j}\neq{i}}{({x}-{x_j})}}{\prod_{{j}\neq{i}}{({x_i}-{x_j})}}{y_i}}} [/latex] code:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>

using namespace std;
const int maxn = 2005;
int A,n,mod;
int add(int x,int y) { x+=y; return x>=mod?x-mod:x; }
int sub(int x,int y) { x-=y; return x<0?x+mod:x; }
int mul(int x,int y) { return 1ll*x*y%mod; }
int ksm(int a,int b) {
	int ans = 1;
	for(;b;b>>=1,a=mul(a,a)) 
		if(b&1) ans=mul(ans,a);
	return ans;
}
int f[maxn][maxn];
int main() {
	scanf("%d%d%d",&A,&n,&mod);
	f[0][0] = 1;
	for(int i=1;i<=2*n;i++) {
		f[i][0] = 1;
		for(int j=1;j<=n;j++) {
			f[i][j] = add(f[i-1][j],mul(i,mul(j,f[i-1][j-1])));
		}
	}
	if(A<=2*n) {
		printf("%d\n",f[A][n]);
		return 0;
	}
	int ans = 0;
	for(int i=0;i<=2*n;i++) {
		int t1 = 1 , t2 = 1;
		for(int j=0;j<=2*n;j++) {
			if(i!=j) {
				t1 = mul(t1,sub(A,j));
				t2 = mul(t2,sub(i,j));
			}
		}
		ans = add(ans,mul(f[i][n],mul(t1,ksm(t2,mod-2))));
	}
	printf("%d",ans);
}
 
posted @ 2018-12-23 21:45  Newuser233  阅读(8)  评论(0)    收藏  举报