Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
Try to ignore the exact order of past meetings. If you only know the subset of fish that are still alive, is any other information actually needed to describe the future?
Each day exactly one fish disappears, so from a state with alive fish, you only move to states of size . That means the subset states form a DAG, which is screaming for DP on bitmasks.
Define as the probability that after exactly eliminations, the alive set is exactly . The start state is the full set: .
For a fixed alive set with and a fish , compute the probability that dies next. There are equally likely unordered meetings. Fish dies if it meets some and eats .
The transition is
Iterate masks downward from the full mask, and the answer for fish is just . That's the whole damn problem.
The only thing that matters is who is alive right now. The exact order of previous meetings does not matter at all. Once the alive set is fixed, the future depends only on that set and the matrix .
So yes, this is a bitmask DP. The tags weren't trolling you.
Let be a subset of fish, represented by a bitmask. Define
This is well-defined because each day exactly one fish dies, so a subset of size can only appear after deaths. No cycles, no revisits, no weird nonsense.
Initially, all fish are alive:
The final answer for fish is simply the probability that only fish remains:
Suppose the current alive set is and . The next meeting is chosen uniformly among all unordered pairs of alive fish, so there are
equally likely meetings.
Fix some fish . When does die next? It dies if it meets some other alive fish and then eats .
For a fixed killer , the pair is chosen with probability , and then kills with probability . Summing over all possible killers gives
Therefore the DP transition is
That is the whole trick. Seriously. Just do not botch the denominator: the meeting is over unordered pairs, so it is , not .
Every transition removes exactly one fish, so it always goes from a larger mask to a smaller mask. Therefore we can process masks in decreasing order, starting from the full mask.
This process is a Markov chain on subsets of alive fish.
By induction over decreasing subset size, is precisely the probability that the set of alive fish is exactly after eliminations. In particular, singleton masks give the probabilities of being the last surviving fish.
There are masks. For each mask, we try each possible victim and sum over each possible killer , so the time complexity is
Memory usage is
With , this is easily fine.
You can precompute some sums to shave constants, but honestly, that is just competitive-programming vanity at this point. The plain DP gets Accepted.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int n;
cin >> n;
vector<vector<long double>> a(n, vector<long double>(n));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
cin >> a[i][j];
}
}
int full = (1 << n) - 1;
vector<long double> dp(1 << n, 0.0L);
dp[full] = 1.0L;
for (int mask = full; mask > 0; --mask) {
int k = std::popcount((unsigned)mask);
if (k <= 1) continue;
long double pairs = 1.0L * k * (k - 1) / 2.0L;
for (int j = 0; j < n; ++j) {
if ((mask & (1 << j)) == 0) continue;
long double dieProb = 0.0L;
for (int i = 0; i < n; ++i) {
if (i == j) continue;
if (mask & (1 << i)) {
dieProb += a[i][j];
}
}
dp[mask ^ (1 << j)] += dp[mask] * dieProb / pairs;
}
}
cout << fixed << setprecision(10);
for (int i = 0; i < n; ++i) {
cout << dp[1 << i] << (i + 1 == n ? '\n' : ' ');
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int n;
cin >> n;
vector<vector<long double>> a(n, vector<long double>(n));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
cin >> a[i][j];
}
}
int full = (1 << n) - 1;
vector<long double> dp(1 << n, 0.0L);
dp[full] = 1.0L;
for (int mask = full; mask > 0; --mask) {
int k = std::popcount((unsigned)mask);
if (k <= 1) continue;
long double pairs = 1.0L * k * (k - 1) / 2.0L;
for (int j = 0; j < n; ++j) {
if ((mask & (1 << j)) == 0) continue;
long double dieProb = 0.0L;
for (int i = 0; i < n; ++i) {
if (i == j) continue;
if (mask & (1 << i)) {
dieProb += a[i][j];
}
}
dp[mask ^ (1 << j)] += dp[mask] * dieProb / pairs;
}
}
cout << fixed << setprecision(10);
for (int i = 0; i < n; ++i) {
cout << dp[1 << i] << (i + 1 == n ? '\n' : ' ');
}
return 0;
}