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.
Group the values by their gcd with . If a position is constrained to , then it must receive a value from the class How large is ?
The size of class is If currently positions demand class , then valid permutations exist iff for every divisor of . Count the number of valid permutations by: choose/order values for constrained positions of each class, then permute all leftover values on free positions.
For inversions, stop trying to analyze all classes separately β that road is pain. Look at the involution on values For every , we have So applying to every value of a valid permutation gives another valid permutation. What does this do to the order of any two values ?
That involution flips every comparison among values . So across all valid permutations, every pair not involving value contributes inversions exactly half the time. Boom: the only asymmetric thing is the value itself. If is forced to some position , its contribution is exactly . If it is not forced, where can it go?
If no query used , then value is just one of the leftover values, so among all valid permutations it is uniformly distributed over the free positions. Let be the number of constrained positions, , and be the sum of constrained indices. Then Hence
n-p_n, & \text{if } n \text{ is forced at } p_n,\\[4pt] \dfrac{\sum_{i\in \text{free}}(n-i)}{f}, & \text{otherwise.} \end{cases}$$ Multiply this expectation by the number of valid permutations $$W=f!\prod_x \frac{|C_x|!}{(|C_x|-m_x)!},$$ which updates in $O(1)$ per query by $$W \leftarrow W\cdot \frac{|C_x|-m_x}{n-k}.$$Let where is a divisor of .
A position with must receive a value from . A free position () can receive anything leftover.
The size of class is because every can be written as
Let be the number of currently constrained positions requiring class . Then valid permutations exist iff
No magic here: if some class is overbooked, you're cooked.
Suppose the current state is valid. Let be the number of constrained and free positions.
For each divisor :
So the number of valid permutations is
This formula is exactly what we need, because after adding one new constraint , only one class changes and decreases by .
If before the query the class had constrained positions, then
That is a sweet update.
If , then , and from now on the answer is forever . No resurrection arc here.
Now for the inversion sum. Doing it class-by-class looks tempting, and also like a great way to waste your afternoon.
Define the involution on values:
n-v,& v<n,\\ n,& v=n. \end{cases}$$ For every $v<n$, $$\gcd(T(v),n)=\gcd(n-v,n)=\gcd(v,n),$$ so if a value $v<n$ is allowed at some constrained position, then $n-v$ is allowed there too. Also, if a position requires $a_i=n$, the only possible value is $n$, and $T(n)=n$, so that is preserved as well. Therefore, applying $T$ to every value of a valid permutation gives **another valid permutation**. Now look at any two values $u,v<n$. Their order flips: $$u>v \iff n-u < n-v.$$ So for every valid permutation where this pair forms an inversion, there is another valid permutation where it does not. ### Consequence Every pair of values not involving $n$ contributes inversions in exactly half of all valid permutations. There are $n-1$ values different from $n$, so the total contribution of all such pairs is $$W \cdot \frac{\binom{n-1}{2}}{2}.$$ That leaves exactly one troublemaker: the value $n$. --- ## 4. Contribution of the value $n$ Since $n$ is the largest value, if it is placed at position $p$, it contributes exactly $$n-p$$ inversions: one with every later position. So we only need to know where $n$ can be. ### Case 1: some query used $x=n$ Then that position must contain value $n$. There is only one such value, since $$|C_n|=\varphi(1)=1.$$ Let its position be $p_n$. Then every valid permutation has the same contribution from $n$: $$n-p_n.$$ So $$\text{InvSum} = W \left( \frac{\binom{n-1}{2}}{2} + (n-p_n) \right).$$ ### Case 2: no query used $x=n$ Then value $n$ is among the leftover values, hence it is placed on a **free** position. Among all valid permutations, all $f$ free positions are symmetric, so $n$ is uniformly distributed over them. Therefore its expected contribution is the average of $n-i$ over free positions $i$: $$\frac{1}{f}\sum_{i\in \text{free}} (n-i).$$ Let $$S = \sum_{i\in \text{constrained}} i.$$ Then $$\sum_{i=1}^n (n-i)=\frac{n(n-1)}{2},$$ and $$\sum_{i\in \text{free}} (n-i) = \frac{n(n-1)}{2} - \sum_{i\in \text{constrained}} (n-i) = \frac{n(n-1)}{2} - kn + S.So in this case
Thatβs it. Seriously. The entire inversion structure collapses to tracking the location of the largest value. Pretty funny, honestly.
We process queries online and maintain:
For a query :
All divisions are modulo , and this is safe because .
We precompute:
That costs for phi (or linear if you prefer), and for factorials/inverses.
Each query is then processed in
So the total complexity is O\!\left(N_{\max}\log\log N_{\max} + \sum q\right), with memory
Very fast, very clean, no cursed data structures required.
#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;
static const int MAXN = 2000000;
vector<int> phi(MAXN + 1), fact(MAXN + 1), invnum(MAXN + 1);
void init() {
for (int i = 0; i <= MAXN; ++i) phi[i] = i;
for (int i = 2; i <= MAXN; ++i) {
if (phi[i] == i) {
for (int j = i; j <= MAXN; j += i) phi[j] -= phi[j] / i;
}
}
phi[1] = 1;
fact[0] = 1;
for (int i = 1; i <= MAXN; ++i) fact[i] = (ll)fact[i - 1] * i % MOD;
invnum[1] = 1;
for (int i = 2; i <= MAXN; ++i) {
invnum[i] = MOD - (ll)(MOD / i) * invnum[MOD % i] % MOD;
}
}
int main() {
setIO();
init();
const ll INV2 = (MOD + 1LL) / 2;
const ll INV4 = INV2 * INV2 % MOD;
int T;
cin >> T;
while (T--) {
int n, q;
cin >> n >> q;
vector<int> cnt(n + 1, 0);
bool dead = false;
ll ways = fact[n];
int k = 0; // constrained positions count
ll sumPos = 0; // sum of constrained indices
int posN = -1; // if value n is forced, its position
ll base = 0;
if (n >= 2) {
base = (ll)(n - 1) % MOD * (n - 2) % MOD * INV4 % MOD;
}
ll totalLaterAll = 1LL * n * (n - 1) / 2; // sum_{i=1}^n (n-i)
for (int qq = 0; qq < q; ++qq) {
int i, x;
cin >> i >> x;
if (dead) {
cout << 0 << '\n';
continue;
}
int cap = phi[n / x];
int freeBefore = n - k;
int available = cap - cnt[x];
ways = ways * available % MOD * invnum[freeBefore] % MOD;
++cnt[x];
++k;
sumPos += i;
if (x == n) {
if (posN != -1) dead = true;
posN = i;
}
if (cnt[x] > cap || ways == 0) {
dead = true;
cout << 0 << '\n';
continue;
}
ll add;
if (posN != -1) {
add = (n - posN) % MOD;
} else {
int freeNow = n - k;
ll freeLater = totalLaterAll - 1LL * k * n + sumPos;
freeLater %= MOD;
if (freeLater < 0) freeLater += MOD;
add = freeLater * invnum[freeNow] % MOD;
}
ll ans = ways * ((base + add) % MOD) % MOD;
cout << ans << '\n';
}
}
}#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;
static const int MAXN = 2000000;
vector<int> phi(MAXN + 1), fact(MAXN + 1), invnum(MAXN + 1);
void init() {
for (int i = 0; i <= MAXN; ++i) phi[i] = i;
for (int i = 2; i <= MAXN; ++i) {
if (phi[i] == i) {
for (int j = i; j <= MAXN; j += i) phi[j] -= phi[j] / i;
}
}
phi[1] = 1;
fact[0] = 1;
for (int i = 1; i <= MAXN; ++i) fact[i] = (ll)fact[i - 1] * i % MOD;
invnum[1] = 1;
for (int i = 2; i <= MAXN; ++i) {
invnum[i] = MOD - (ll)(MOD / i) * invnum[MOD % i] % MOD;
}
}
int main() {
setIO();
init();
const ll INV2 = (MOD + 1LL) / 2;
const ll INV4 = INV2 * INV2 % MOD;
int T;
cin >> T;
while (T--) {
int n, q;
cin >> n >> q;
vector<int> cnt(n + 1, 0);
bool dead = false;
ll ways = fact[n];
int k = 0; // constrained positions count
ll sumPos = 0; // sum of constrained indices
int posN = -1; // if value n is forced, its position
ll base = 0;
if (n >= 2) {
base = (ll)(n - 1) % MOD * (n - 2) % MOD * INV4 % MOD;
}
ll totalLaterAll = 1LL * n * (n - 1) / 2; // sum_{i=1}^n (n-i)
for (int qq = 0; qq < q; ++qq) {
int i, x;
cin >> i >> x;
if (dead) {
cout << 0 << '\n';
continue;
}
int cap = phi[n / x];
int freeBefore = n - k;
int available = cap - cnt[x];
ways = ways * available % MOD * invnum[freeBefore] % MOD;
++cnt[x];
++k;
sumPos += i;
if (x == n) {
if (posN != -1) dead = true;
posN = i;
}
if (cnt[x] > cap || ways == 0) {
dead = true;
cout << 0 << '\n';
continue;
}
ll add;
if (posN != -1) {
add = (n - posN) % MOD;
} else {
int freeNow = n - k;
ll freeLater = totalLaterAll - 1LL * k * n + sumPos;
freeLater %= MOD;
if (freeLater < 0) freeLater += MOD;
add = freeLater * invnum[freeNow] % MOD;
}
ll ans = ways * ((base + add) % MOD) % MOD;
cout << ans << '\n';
}
}
}