Cần biết trước
- Nguyên lí bao hàm loại trừ
- Bài toán Chia kẹo Euler
Đề bài
Tham khảo
Tóm tắt
Cho hai số nguyên $n, k$. Đếm số lượng hoán vị của dãy ${1, 2, \dots, n}$ có $k$ cặp nghịch thế.
- Subtask 1: $1 \leq n \leq 3000, 0 \leq k \leq \min(C^2_n, 3000)$
- Subtask 2: $1 \leq n \leq 10^5, 0 \leq k \leq \min(C^2_n, 10^5)$
Phân tích
Subtask 1: Quy hoạch động
Độ phức tạp: $\mathcal{O}(nk)$
Trước khi đến với lời giải chính của bài này. Ta xét một hướng tiếp cận quy hoạch động khi $n$ và $k$ nhỏ. Gọi $\text{dp}[i][j]$ là số lượng hoán vị của dãy ${1, 2, \dots, i}$ có $j$ nghịch thế, ta dễ có công thức sau: $$ \text{dp}[i][j] = \sum_{x = 0}^{i-1} \text{dp}[i-1][j-x] $$
Giải thích công thức
Ở một trạng thái $(i, j)$ của hàm quy hoạch động, ta có thể đặt phần tử $i$ vào một trong $i$ vị trí của hoán vị ${1, 2, \dots, i-1}$ đã dựng trước đó. Số nghịch thế thay đổi như sau:
- Đặt vào vị trí cuối dãy, số nghịch thế không thay đổi.
- Đặt vào trước phần tử cuối, số nghịch thế tăng lên $1$ do phần tử $i$ lớn hơn các phần tử đã điền và có 1 phần tử đứng sau $i$.
- …
- Đặt vào vị trí đầu dãy, số nghịch thế tăng lên $i-1$ do có $i-1$ phần tử nhỏ hơn $i$ và đứng sau $i$.
Tóm lại, khi đặt phần tử $i$ vào một vị trí trong hoán vị đã dựng, số nghịch thế tăng lên sẽ bằng số phần tử đứng sau $i$, do $i$ lớn hơn tất cả phần tử đã điền.
Đáp án của bài toán sẽ là $\text{dp}[n][k]$. Ta sẽ được thuật toán có độ phức tạp là $\mathcal{O}(nk^2)$, từ đó ta có thể dùng mảng cộng dồn để cải tiến thành $\mathcal{O}(nk)$.
Subtask 2: Bao hàm loại trừ
Độ phức tạp: $\mathcal{O}(k\sqrt{k})$
Nhận xét
Trong công thức quy hoạch động trên, ta nhận thấy khi thêm phần tử thứ $i$ vào hoán vị, số cặp nghịch thế lại tăng thêm. Ta gọi lượng tăng thêm đó là $x_i$. Khi đó, bài toán trở thành đếm số dãy số $(x_1, x_2, \dots, x_n)$ phân biệt thỏa mãn: $$ x_1 + x_2 + \dots + x_n = k \text { và } 0 \leq x_i \leq i-1 $$
Xét phiên bản đơn giản hơn của bài toán: tìm số lượng nghiệm nhưng không có cận trên của $x_i$. Ta dễ dàng dùng công thức Chia kẹo Euler và tìm được đáp án của bài toán là $C_{n-1+k}^{n-1}$. Như vậy, ta cần phải tìm cách loại bỏ điều kiện $x_i \leq i-1$ để có thể dùng công thức trên.
Lời giải
Gọi $f(j)$ là số nghiệm của bài toán sao cho có ít nhất $j$ phần tử $x_i \geq i$ (phần tử xấu). Giả sử, ta xác định được $j$ phần tử xấu $x_{i_1} \geq i_1, x_{i_2} \geq i_2, \dots, x_{i_j} \geq i_j$ và $s = i_1 + i_2 + \dots + i_j$. Khi đó, ta gọi dãy $y$ sao cho $y_i = x_i - i$ nếu $x_i$ là phần tử xấu, ngược lại ta cho $y_i = x_i$. Như vậy bài toán trở thành đếm số dãy $y$ thỏa mãn: $$ y_1 + y_2 + \dots + y_n = k - s \text { và } y_i \geq 0 $$ Áp dụng Chia kẹo Euler, số lượng dãy thỏa mãn trong trường hợp này là $C^{n-1}_{n-1+(k-s)}$
Như vậy ta có cách tính $f(j)$ như sau: Gọi $g(s, j)$ là số cách chọn $j$ số nguyên phân biệt thuộc $[1, n]$ sao cho tổng của chúng là $s$. Khi đó: $$f(j) = \sum_{s = 0}^{k} g(s, j)\times C^{n-1}_{n-1+k-s}$$
Ta nhận thấy do $j$ số nguyên phân biệt nên $j \leq \sqrt{k}$, vì vậy ta có thể tính $g(s, j)$ bằng quy hoạch động trong $O(k\sqrt{k})$ theo công thức: $$ g(s, j) = g(s-j, j) + g(s-j, j-1) - g(s-(n+1), j-1) $$
Giải thích công thức
Tại trạng thái $(s, j)$ - $j$ phần tử có tổng là $s$, ta có hai trường hợp chuyển trạng thái:
- $g(s-j, j)$: Tăng tất cả phần tử, mỗi phần tử thêm $1$ đơn vị.
- $g(s-j, j-1) - g(s-(n+1), j-1)$: Tăng tất cả phần tử trước đó, mỗi phần tử thêm $1$ đơn vị, và tạo thêm một phần tử mới có giá trị là $1$. Sau đó, ta phải loại đi các trường hợp dãy có phần tử lớn hơn $n$.
Cuối cùng, áp dụng nguyên lí bao hàm loại trừ, ta có công thức cho đáp án như sau: $$ \text{answer} = \sum_{j = 0}^{\sqrt{k}} (-1)^j f(j) $$
Code tham khảo
Hãy xem code khi thực sự cần thiết
|
|