首页 > 代码库 > bzoj[2655] calc
bzoj[2655] calc
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
Solution
可以得出一个时间复杂度爆炸的dp方程
f[i][j] = f[i - 1][j - 1] * j + f[i - 1][j]
f[i][j]表示从区间[1,j]中选出i个数得出的序列的乘积和
所以把算出500递推值,之后用lagrange插值拟合多项式,再把A带进去求值就好
#include <stdio.h>typedef long long ll;int m, n, p, tot;ll f[1510][510], fac[510], x[1010], y[1010];ll Pow(register ll t, register int k) { register ll tmp = 1; for(; k; k >>= 1, t = t * t % p) if(k & 1) tmp = tmp * t % p; return tmp; }ll Lagrange() { ll ans = 0, a = 1, b; for(register int i = 0; i < tot; i++) a = a * (m - x[i] + p) % p; for(register int i = 0; i < tot; ans = (ans + a * Pow(b, p - 2) % p * y[i]) % p, i++) { b = (m - x[i] + p) % p; for(register int j = 0; j < tot; j++) if(i != j) b = b * (x[i] - x[j] + p) % p; } return ans; }int main() { scanf("%d%d%d", &m, &n, &p); fac[0] = 1; for(register int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % p; f[0][0] = 1; for(register int i = 1; i <= n * 3 + 5; i++) for(register int j = 0; j <= n + 5; j++) f[i][j] = j ? (f[i - 1][j - 1] * i + f[i - 1][j]) % p : f[i - 1][j]; for(register int i = 1; tot <= (n << 1 | 1); i++) if(f[i][n] && i ^ m) x[tot] = i, y[tot++] = f[i][n]; printf("%lld\n", Lagrange() * fac[n] % p); return 0; }
bzoj[2655] calc
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。