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.
Use linearity of expectation. You do not need to reason about the whole random permutation at once. For each pair , compute
and sum over all pairs.
For a fixed pair , the values are just a uniformly random ordered pair of distinct positions from . So the contribution of is
The rest of the permutation is irrelevant. Nice and clean.
That annoying condition is the only thing making the formula ugly. So do the classic competitive-programming crime: add the cases back in, count everything, then subtract them later.
If , then
because all are positive. So the total extra stuff you added is exactly
After adding back , define all products
Now you are counting
That is: among all numbers, each tagged by its original index , count pairs where the earlier index has the larger value.
Build the list of items for all . Sort them by value in descending order. Process equal values in one batch.
Maintain a Fenwick tree over indices storing how many already-processed items came from each index. For an item from index , the number of valid larger items is just the prefix sum up to .
So:
to remove the fake cases. 5. Multiply by modulo .
That’s the whole trick. Sneaky bastard, but fair.
Let
where is the indicator function. Then
So we only need the inversion probability for one fixed pair .
For fixed , the pair is uniformly distributed over all ordered pairs of distinct positions of the original array . Each such ordered pair appears in exactly permutations, so it’s uniform. Therefore
Hence the expected number of inversions is
If you try to compute that pair-by-pair directly, you get smacked by an extra factor of . So we need a better view.
Define
The answer is just
Now define the easier quantity
where we also allow .
What did we overcount? Exactly the cases with :
because every is positive.
So for each pair , the fake contribution from is either or , and globally
where
That positivity condition is doing real work here. If had zeros or negatives, life would be much more annoying. Thankfully, the problem is not that evil.
For every index and every position of , create one item
There are such items.
Then
In words:
That’s suddenly a very standard offline counting problem.
Sort all items by value in descending order.
Suppose we are processing an item from index . Any item already processed has value strictly larger — except for equal values, which must not be counted because the inequality is strict.
So the correct way is:
If the Fenwick tree stores how many processed items came from each index, then for an item from index , its contribution is
where is the number of processed items with index at most .
Summing this over all items gives exactly .
Why? Because every counted pair is:
That’s the whole trick. The problem looks like probability soup, but underneath it’s just an inversion-like count on generated numbers.
For one test case:
We prove the algorithm computes the expected number of inversions.
For each pair ,
By linearity of expectation,
So it is enough to compute the integer numerator
Now define
The only difference is the terms. Since ,
There are choices of , hence
Finally, equals the number of pairs of items with and . After sorting all items by decreasing value, an item contributes exactly the number of already processed items coming from smaller indices. Equal values must be processed as a batch so they do not count each other. Therefore the Fenwick sweep computes exactly .
Thus the algorithm returns the exact expected value modulo .
Let .
So the total is
Memory usage is .
Because the sum of over all test cases is at most , we have
so this is easily fine.
Yeah, the intended trick is basically: turn the probability problem into one giant offline inversion count. Once you see that, the rest is just bookkeeping.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
static const int MOD = 998244353;
ll mod_pow(ll a, ll e) {
ll r = 1;
while (e > 0) {
if (e & 1) r = r * a % MOD;
a = a * a % MOD;
e >>= 1;
}
return r;
}
struct Fenwick {
int n;
vector<int> bit;
Fenwick() : n(0) {}
Fenwick(int n_) { init(n_); }
void init(int n_) {
n = n_;
bit.assign(n + 1, 0);
}
void add(int idx, int val) {
for (; idx <= n; idx += idx & -idx) bit[idx] += val;
}
int sum(int idx) const {
int res = 0;
for (; idx > 0; idx -= idx & -idx) res += bit[idx];
return res;
}
};
struct Item {
ll val;
int idx;
};
int main() {
setIO();
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<ll> a(n), b(n);
for (int i = 0; i < n; ++i) cin >> a[i];
for (int i = 0; i < n; ++i) cin >> b[i];
if (n == 1) {
cout << 0 << '\n';
continue;
}
vector<Item> items;
items.reserve(1LL * n * n);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
items.push_back({a[i] * b[j], i + 1});
}
}
sort(items.begin(), items.end(), [](const Item& x, const Item& y) {
return x.val > y.val;
});
Fenwick fw(n);
ll Tprime = 0;
int m = (int)items.size();
for (int l = 0; l < m; ) {
int r = l;
while (r < m && items[r].val == items[l].val) ++r;
for (int k = l; k < r; ++k) {
Tprime += fw.sum(items[k].idx - 1);
}
for (int k = l; k < r; ++k) {
fw.add(items[k].idx, 1);
}
l = r;
}
ll invA = 0;
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
if (a[i] > a[j]) ++invA;
}
}
ll T = Tprime - 1LL * n * invA;
ll denom = 1LL * n * (n - 1) % MOD;
ll ans = (T % MOD + MOD) % MOD * mod_pow(denom, MOD - 2) % MOD;
cout << ans << '\n';
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
static const int MOD = 998244353;
ll mod_pow(ll a, ll e) {
ll r = 1;
while (e > 0) {
if (e & 1) r = r * a % MOD;
a = a * a % MOD;
e >>= 1;
}
return r;
}
struct Fenwick {
int n;
vector<int> bit;
Fenwick() : n(0) {}
Fenwick(int n_) { init(n_); }
void init(int n_) {
n = n_;
bit.assign(n + 1, 0);
}
void add(int idx, int val) {
for (; idx <= n; idx += idx & -idx) bit[idx] += val;
}
int sum(int idx) const {
int res = 0;
for (; idx > 0; idx -= idx & -idx) res += bit[idx];
return res;
}
};
struct Item {
ll val;
int idx;
};
int main() {
setIO();
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<ll> a(n), b(n);
for (int i = 0; i < n; ++i) cin >> a[i];
for (int i = 0; i < n; ++i) cin >> b[i];
if (n == 1) {
cout << 0 << '\n';
continue;
}
vector<Item> items;
items.reserve(1LL * n * n);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
items.push_back({a[i] * b[j], i + 1});
}
}
sort(items.begin(), items.end(), [](const Item& x, const Item& y) {
return x.val > y.val;
});
Fenwick fw(n);
ll Tprime = 0;
int m = (int)items.size();
for (int l = 0; l < m; ) {
int r = l;
while (r < m && items[r].val == items[l].val) ++r;
for (int k = l; k < r; ++k) {
Tprime += fw.sum(items[k].idx - 1);
}
for (int k = l; k < r; ++k) {
fw.add(items[k].idx, 1);
}
l = r;
}
ll invA = 0;
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
if (a[i] > a[j]) ++invA;
}
}
ll T = Tprime - 1LL * n * invA;
ll denom = 1LL * n * (n - 1) % MOD;
ll ans = (T % MOD + MOD) % MOD * mod_pow(denom, MOD - 2) % MOD;
cout << ans << '\n';
}
return 0;
}