JZOJ 4308.長壽花
阿新 • • 發佈:2020-07-31
題面
思路
這種題當然要 \(dp\) 啦
設 \(g_{i,j}\) 表示前 \(i\) 個位置用指定的 \(j\) 種顏色裝飾(即用顏色 \(1..j\) 來裝飾)
那麼 \(g_{i,j}=g_{i-1,j}*(j-1)+g_{i-1,j-1}*j\)
前一項表示前 \(i-1\) 用了 \(j\) 種顏色,那麼當前位可以用 \(j-1\) 種顏色,因為它和前面一個不能相同
後一項表示前 \(i-1\) 用了 \(j-1\) 種顏色,根據定義,前 \(i-1\) 位用的顏色是 \(1..j-1\),而現在多了一種來用,那麼在 \(i\) 這個階段的前 \(i-1\) 位用 \(j-1\)
再設 \(f_{i,j}\) 表示前 \(i\) 層放了裝飾品且第 \(i\) 層選 \(j\) 種顏色的裝飾品的方案數
那麼 \(f_{i,j}={\sum_{k=1}^{a_{i-1}}f_{i-1,k}*C_{m}^{j}*g_{a_i,j}}-f_{i-1,j}*g_{a_i,j}\)
意思是前 \(i-1\) 層放的方案乘上本層 \(a_i\) 個位置選 \(j\) 種顏色的方案(乘法原理),因為 \(g\)
而題中規定本層與上一層顏色去重後的集合不能相同,所以我們再減去 \(f_{i-1,j}*g_{a_i,j}\) 即為前一個式子重複算的數量
而本題更噁心的是模數不一定是質數,所以再算組合數時我們需要質因數分解,加點奇技淫巧避免時間和空間裂開
看我們算 \(C\) 的過程,顯然算 \(C_{m}^{j+1}\) 時可以從 \(C_{m}^j\) 處推來
所以我們分解質因數後存的東西不用清零,直接指數該加的加,該減的減
最後快速冪算一下剩下的指數和底數的貢獻就行了
#include<cstdio> #include<cstring> #include<iostream> using namespace std; typedef long long LL; const int M = 5005; int n , m , a[1000005] , Mx , o , tot , pr[M] , vis[1000005] , num[1000005] , s[1000005] , cnt; LL p , g[M][M] , f[3][M] , c[1000005] , sum , ans; inline void getprime(int m) { vis[0] = vis[1] = 1; for(register int i = 2; i <= m; i++) { if (!vis[i]) pr[++tot] = i; for(register int j = 1; j <= tot && pr[j] * i <= m; j++) { vis[pr[j] * i] = 1; if (i % pr[j] == 0) break; } } } inline LL fpow(LL x , int y) { LL res = 1; while (y) { if (y & 1) res = res * x % p; y >>= 1 , x = x * x % p; } return res; } inline void up(int x) { for(register int i = 1; i <= tot && pr[i] * pr[i] <= x; i++) if (x % pr[i] == 0) { if (!vis[pr[i]]) vis[pr[i]] = 1 , num[++cnt] = pr[i]; while (x % pr[i] == 0) s[pr[i]]++ , x = x / pr[i]; } if (x > 1) { if (!vis[x]) num[++cnt] = x , vis[x] = 1; s[x]++; } } inline void down(int x) { for(register int i = 1; i <= tot && pr[i] * pr[i] <= x; i++) if (x % pr[i] == 0) { while (x % pr[i] == 0) s[pr[i]]-- , x = x / pr[i]; } if (x > 1) s[x]--; } inline LL getc(int x , int y) { LL res = 1; up(y) , down(x); for(register int i = 1; i <= cnt; i++) res = res * fpow((LL)num[i] , s[num[i]]) % p; return res; } int main() { freopen("kalanchoe.in" , "r" , stdin); freopen("kalanchoe.out" , "w" , stdout); scanf("%d%d%lld" , &n , &m , &p); for(register int i = 1; i <= n; i++) scanf("%d" , &a[i]) , Mx = max(Mx , a[i]); g[1][1] = 1; for(register int i = 2; i <= Mx; i++) for(register int j = 1; j <= i; j++) g[i][j] = (g[i - 1][j] * (j - 1) % p + g[i - 1][j - 1] * j % p) % p; getprime(m + 3); memset(vis , 0 , sizeof vis); for(register int i = 1; i <= min(m , Mx); i++) c[i] = getc(i , m - i + 1); sum = 1; for(register int i = 1; i <= n; i++) { o = 1 - o; for(register int j = 1; j <= min(a[i] , m); j++) f[o][j] = ((sum * c[j] % p * g[a[i]][j] % p - f[1 - o][j] * g[a[i]][j] % p) % p + p) % p; sum = 0; for(register int j = 1; j <= min(a[i] , m); j++) sum = (sum + f[o][j]) % p; if (i > 1) for(register int j = a[i] + 1; j <= a[i - 2]; j++) f[o][j] = 0; } for(register int i = 1; i <= min(a[n] , m); i++) ans = (ans + f[o][i]) % p; printf("%lld" , ans); }