D - ボールの箱詰め / Packing Balls into Boxes 解説 by admin
gemini-3.5-flash-thinkingOverview
This is a problem of distributing \(K\) distinguishable balls into \(N\) distinguishable boxes such that “no box is empty,” while satisfying certain constraints. Due to the constraints, some balls must be placed in the same box.
Analysis
1. Organizing the Constraints (Extracting Connected Components)
The constraint “Ball \(U_j\) and Ball \(V_j\) must be placed in the same box” implies grouping balls together. This relationship is transitive (if Ball \(A\) and \(B\) are in the same box, and Ball \(B\) and \(C\) are in the same box, then Ball \(A\) and \(C\) must also be in the same box), so we can classify balls into several groups.
This reduces to finding connected components in a graph where balls are vertices and constraints are edges, or equivalently, grouping using the Union-Find (DSU) data structure.
2. Simplifying the Problem
Suppose that after grouping the balls, we obtain \(C\) groups in total. Since balls in the same group always behave identically (they go into the same box), we can treat each group as “one large ball.”
This allows us to rephrase the problem simply as follows: “Find the total number of ways to distribute \(C\) distinguishable groups into \(N\) distinguishable boxes such that no box is empty.”
3. Distributions with No Empty Boxes (Inclusion-Exclusion Principle)
If \(C < N\), then the number of groups is fewer than the number of boxes, so no matter how we distribute them, there will always be at least one empty box. Therefore, the answer in this case is \(0\).
When \(C \ge N\), we need to count the total number of distributions with no empty boxes. This can be efficiently computed using the Inclusion-Exclusion Principle.
We focus on the number of empty boxes: - The number of ways to freely distribute into all \(N\) boxes is \(N^C\). - From this, we exclude the excess assignments where “at least one box is empty” using the inclusion-exclusion principle.
Specifically, letting \(i\) be the number of boxes that are empty, the total number of valid distributions is given by the following formula: $\( \text{Total number of ways} = \sum_{i=0}^{N} (-1)^i \binom{N}{i} (N-i)^C \)$
The meaning of each term is as follows: - \(\binom{N}{i}\): The number of ways to choose the \(i\) boxes that are empty - \((N-i)^C\): The number of ways to freely assign \(C\) groups to the remaining \(N-i\) boxes (since each group has \(N-i\) choices, there are \((N-i)^C\) ways) - \((-1)^i\): The sign from the inclusion-exclusion principle (subtract when \(i\) is odd, add when \(i\) is even)
Algorithm
- Grouping (Union-Find)
Prepare a Union-Find with balls as vertices. Perform merge operations (
merge) for the given \(M\) constraints \((U_j, V_j)\), and determine the final number of connected components \(C\). - Handling Corner Cases
If \(C < N\), it is impossible to have no empty boxes, so output
0and terminate. - Precomputation of Factorial Tables
To quickly compute the binomial coefficients \(\binom{N}{i}\), precompute tables of factorials (
fact) and inverse factorials (inv_fact) in \(O(N)\). - Inclusion-Exclusion Computation
Loop from \(i = 0\) to \(N\), computing \(\binom{N}{i} (N-i)^C\) at each step, and add or subtract considering the sign. The computation of \((N-i)^C\) is done efficiently using fast exponentiation (
pow).
Complexity
Time Complexity: \(O(K + M \alpha(K) + N \log C)\)
- Merging in Union-Find and counting connected components takes \(O(K + M \alpha(K))\) (where \(\alpha\) is the inverse Ackermann function).
- Building the factorial table takes \(O(N)\).
- The inclusion-exclusion loop runs \(N\) times, and at each iteration \((N-i)^C\) is computed in \(O(\log C)\), so the entire loop takes \(O(N \log C)\).
- Overall, under the constraints \(N, K, M \le 2 \times 10^5\), this comfortably fits within the time limit.
Space Complexity: \(O(N + K)\)
- Union-Find management uses \(O(K)\) memory, and storing the factorial table uses \(O(N)\) memory.
Implementation Notes
Utilizing a Union-Find (DSU) Library By using
atcoder::dsufrom the AtCoder Library (ACL), connected component management can be implemented concisely and without bugs.Automatic Modular Arithmetic (
modint) Since values become extremely large during computation, performing all calculations onatcoder::modint998244353prevents overflow and automatically computes the remainder modulo \(998244353\).Source Code
#include <iostream>
#include <vector>
#include <atcoder/dsu>
#include <atcoder/modint>
using namespace std;
using namespace atcoder;
using mint = modint998244353;
vector<mint> fact, inv_fact;
void init_fact(int n) {
fact.resize(n + 1);
inv_fact.resize(n + 1);
fact[0] = 1;
for (int i = 1; i <= n; i++) fact[i] = fact[i - 1] * i;
inv_fact[n] = fact[n].inv();
for (int i = n - 1; i >= 0; i--) inv_fact[i] = inv_fact[i + 1] * (i + 1);
}
mint nCr(int n, int r) {
if (r < 0 || r > n) return 0;
return fact[n] * inv_fact[r] * inv_fact[n - r];
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int N, K, M;
if (!(cin >> N >> K >> M)) return 0;
dsu d(K);
for (int i = 0; i < M; i++) {
int u, v;
cin >> u >> v;
u--; v--;
d.merge(u, v);
}
int C = 0;
for (int i = 0; i < K; i++) {
if (d.leader(i) == i) {
C++;
}
}
if (C < N) {
cout << 0 << "\n";
return 0;
}
init_fact(N);
mint ans = 0;
for (int i = 0; i <= N; i++) {
mint term = nCr(N, i) * mint(N - i).pow(C);
if (i % 2 == 1) {
ans -= term;
} else {
ans += term;
}
}
cout << ans.val() << "\n";
return 0;
}
This editorial was generated by gemini-3.5-flash-thinking.
投稿日時:
最終更新: