1. 程式人生 > >Codeforces 840C On the Bench - 動態規劃 - 組合數學

Codeforces 840C On the Bench - 動態規劃 - 組合數學

題目傳送門

  傳送門I

  傳送門II

  傳送門III

題目大意

  給定$n$個數,我們認為它們互不相同,即使它們數值上相等,問存在多少排列方式,使得任意兩個相鄰位置上的數的乘積不是完全平方數。

  顯然一個正整數$x$可以被表示為$d_{1}\cdot s_{1}^{2}$,其中$d,s\in N_{+}$,$d$取最小值。

  另一個正整數$y$表示為$d_{2}\cdot s_{2}^{2}$。那麼$xy$是完全平方數的充分必要條件是$d_{1} = d_{2}$。(可以先證明$d$不含平方因子,然後用反證法證明)

  然後問題可以轉化成有$n$個互不相同的球,每個球被塗有一種顏色,要求將所有球排成一排,同種顏色的球不相鄰的方案數。

  考慮設$f_{i, j}$表示當前考慮到第$i$種顏色,已經有$j$個連續的段。

  每次考慮放入一種顏色的所有球。主要有三種情況:

  1. 一個球被拿去連線兩段球
  2. 一個球被放入了一個獨立的段
  3. 一個球放在某一段的端點處

  我們列舉第一種情況的球的個數。假設第$i$種顏色有$c$個球,原本有$j$段球,現在我要拿$x$球作為第一種情況,拿$y$個球作為第二種情況。(這樣根據之前有多少段計算現在有多少段)

  1. 對於第一部分,首先我需要選擇$x$個球,然後在$j - 1$個間隙中選出$x$個間隙,再決定每個球放在哪個間隙。這一部分方案數是$\binom{c}{x}\binom{j - 1}{x}x!$
  2. 對於第二部分,我需要從剩下的$x - c$個球中選出$y$個球,我先將它排列好,然後分成$j - x$段插入間隙中(這個插入和上面的那種情況不同,這裡不會連線兩端點)。顯然這一部分的方案數是$\binom{c - x}{y}\binom{y + j - x}{j - x}y!$
  3. 對於第三部分,剩下的球已經是確定的了,不用考慮。我只需要從$2(j - x)$個可選的位置中選擇$c - x - y$個位置,然後排列一下把球放上去。

  時間複雜度$O(n^{3})$

Code

  1 /**
  2  * Codeforces
  3  * Problem#840C
  4
* Accepted 5 * Time: 62ms 6 * Memory: 1800k 7 */ 8 #include <algorithm> 9 #include <iostream> 10 #include <cstdlib> 11 #include <cstdio> 12 using namespace std; 13 typedef bool boolean; 14 15 const int N = 305, M = 1e9 + 7; 16 17 int add(int a, int b) { 18 return ((a += b) >= M) ? (a - M) : (a); 19 } 20 21 int mul(int a, int b) { 22 return (a * 1ll * b) % M; 23 } 24 25 void exgcd(int a, int b, int& x, int& y) { 26 if (!b) 27 x = 1, y = 0; 28 else { 29 exgcd(b, a % b, y, x); 30 y -= (a / b) * x; 31 } 32 } 33 34 int inv(int a, int n) { 35 int x, y; 36 exgcd(a, n, x, y); 37 return (x < 0) ? (x + n) : (x); 38 } 39 40 int n; 41 int ar[N]; 42 int f[N][N]; 43 int fac[N], _fac[N]; 44 int C[N << 1][N << 1]; 45 46 inline void init() { 47 scanf("%d", &n); 48 for (int i = 1, x, y; i <= n; i++) { 49 scanf("%d", &x), y = 1; 50 for (int p = 2, a = 0; p * p <= x; p++, a = 0) { 51 while (!(x % p)) 52 x /= p, a ^= 1; 53 if (a) 54 y *= p; 55 } 56 if (x > 1) 57 y *= x; 58 ar[i] = y; 59 } 60 } 61 62 inline void solve() { 63 sort(ar + 1, ar + n + 1); 64 C[0][0] = 1; 65 for (int i = 1; i <= (n << 1); i++) { 66 C[i][0] = C[i][i] = 1; 67 for (int j = 1; j < i; j++) 68 C[i][j] = add(C[i - 1][j - 1], C[i - 1][j]); 69 } 70 71 fac[0] = 1; 72 for (int i = 1; i <= n; i++) 73 fac[i] = mul(fac[i - 1], i); 74 _fac[n] = inv(fac[n], M); 75 for (int i = n; i; i--) 76 _fac[i - 1] = mul(_fac[i], i); 77 78 int t = 1; 79 f[0][0] = 1; 80 for (int i = 1, r = i; i <= n; i = r, t++) { 81 while (r <= n && ar[r] == ar[i]) 82 r++; 83 int cnt = r - i; 84 for (int j = 0; j < i; j++) { 85 for (int k = j; k <= j + cnt; k++) { 86 int c_indep = k - j, c_depen = cnt - c_indep; 87 int n_indep = mul(mul(C[cnt][c_indep], fac[c_indep]), C[c_indep + j][j]); 88 int n_depen = mul(C[j * 2][c_depen], fac[c_depen]); 89 f[t][k] = add(f[t][k], mul(f[t - 1][j], mul(n_indep, n_depen))); 90 } 91 // f[t][k] = add(f[t][k], mul(f[t - 1][j], mul(mul(fac[k], _fac[j]), mul(C[j * 2][cnt - k + j], fac[cnt - k + j])))); 92 for (int con = 1; con < j; con++) { 93 for (int k = j - con; k < r; k++) { 94 int c_indep = k - j + con, c_depen = cnt - con - c_indep; 95 if (c_depen < 0) 96 break; 97 int n_conne = mul(C[j - 1][con], mul(C[cnt][con], fac[con])); 98 int n_indep = mul(C[cnt - con][c_indep], mul(fac[c_indep], C[c_indep + j - con][j - con])); 99 int n_depen = mul(C[(j - con) * 2][c_depen], fac[c_depen]); 100 f[t][k] = add(f[t][k], mul(f[t - 1][j], mul(n_conne, mul(n_indep, n_depen)))); 101 // f[t][k] = add(f[t][k], mul(f[t - 1][j], mul(mul(C[j - 1][con], ), mul(fac[j + ind], mul(_fac[j], C[(j - con) * 2][dep]))))); 102 } 103 } 104 } 105 // for (int i = 0; i < r; i++) 106 // cerr << f[t][i] << " " ; 107 // cerr << '\n'; 108 } 109 printf("%d\n", f[t - 1][1]); 110 } 111 112 int main() { 113 init(); 114 solve(); 115 return 0; 116 }