題庫連接php
給出 \(A,n,p\) ,讓你在模 \(p\) 意義下求全部序列 \(a\) 知足「長度爲 \(n\) 且 \(a_i\in[1,A]\) ,而且對於 \(i\neq j,a_i\neq a_j\)」的價值和。c++
一個序列的價值定義爲 \(\prod\limits_{i=1}^n a_i\) 。ui
\(1\leq A\leq 10^9,1\leq n\leq 500,p\leq 10^9\) 而且 \(p\) 爲素數, \(p>A>n+1\) 。spa
考慮樸素的 \(\text{DP}\) 。.net
記 \(f_{i,j}\) 爲長度爲 \(j\) 的序列只含 \([1,i]\) 內的數的價值。code
轉移是考慮是否加上 \(i\) 這個元素而且若是加上放在哪一位,那麼blog
\[f_{i,j}=f_{i-1,j-1}\times j\times i+f_{i-1,j}\]ip
這樣轉移是 \(O(An)\) 的,過不了...get
聽說 \(f_{i,j}\) 是一個關於 \(i\) 有關的 \(2j\) 次的多項式。數學
具體證實的話,能夠差分以後用數學概括法來證。
那麼咱們考慮 \(\text{DP}\) 出 \(f_{i,n},i\in[0,2n]\) 的值,用拉格朗日插值求出惟一解便可...
#include <bits/stdc++.h> using namespace std; const int N = 1000+5; int f[N][N], a, n, p, x[N], y[N], ifac[N], inv[N]; int quick_pow(int a, int b) { int ans = 1; while (b) { if (b&1) ans = 1ll*ans*a%p; b >>= 1, a = 1ll*a*a%p; } return ans; } int lagrange(int n, int *x, int *y, int xi) { int ans = 0, s1 = 1; for (int i = 0; i <= n; i++) { s1 = 1ll*s1*(xi-x[i])%p; inv[i] = quick_pow(xi-x[i], p-2); } ifac[1] = ifac[0] = 1; for (int i = 2; i <= n; i++) ifac[i] = -1ll*p/i*ifac[p%i]%p; for (int i = 2; i <= n; i++) ifac[i] = 1ll*ifac[i]*ifac[i-1]%p; for (int i = 0; i <= n; i++) (ans += 1ll*y[i]*s1%p*inv[i]%p*ifac[i]%p*(((n-i)&1) ? -1 : 1)*ifac[n-i]%p) %= p; return (ans+p)%p; } void work() { scanf("%d%d%d", &a, &n, &p); for (int i = 0; i <= n*2; i++) f[i][0] = 1; for (int i = 1; i <= n*2; i++) for (int j = 1; j <= n; j++) f[i][j] = (1ll*f[i-1][j-1]*i%p*j%p+f[i-1][j])%p; if (a <= 2*n) {printf("%d\n", f[a][n]); return; } for (int i = 0; i <= 2*n; i++) x[i] = i, y[i] = f[i][n]; printf("%d\n", lagrange(2*n, x, y, a)); } int main() {work(); return 0; }