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.
Turn every item into a point . A direct trade from to is missing only in the clean northeast case: and .
Sort items by . If is the -value of the item whose -value is , then the direct edge rule becomes: from position you can go to position iff or .
A split after is a real wall exactly when the first positions contain the first values. In permutation language: Then nothing on the left can ever reach anything on the right.
Those wall positions split the graph into SCCs. If the SCC sizes are from left to right, reachability goes inside an SCC and from right SCCs to left SCCs, so the answer is
Maintain the wall positions dynamically. For every position , draw the interval between and . A wall after exists iff no interval crosses gap . Swapping two values of deletes two intervals and inserts two intervals, so this becomes range add on gap-cover counts plus maintaining where the count is zero.
Let item be the point .
A direct trade exists iff
So the only missing direct edge is when is strictly northeast of :
That is the whole geometry of the problem.
Normalize one permutation away
Sort items by increasing . Define
Now each item is just a position with value , and a direct edge exists iff
Moving left is always possible using the first trader. Moving to a smaller -value is always possible using the second trader.
Cuts / walls
Look at a split after position .
If
then the first positions contain exactly the values .
So every point on the left has both coordinates smaller than every point on the right. No left item has a direct edge to a right item. Also, no path can cross from left to right, because at some step it would need a first left-to-right edge. Dead end. Bonk.
On the other hand, every right item can directly go to every left item, because its position is larger.
So these cut positions separate SCCs in a line.
Why blocks between cuts are SCCs
Suppose there is no cut inside a block , while and are cuts.
For any internal split with , since is not a cut, the left side cannot contain exactly the smallest available values. Therefore there is an inversion crossing the split:
Then by the second trader.
Also, anything on the right can go left directly by position. This gives reachability across every internal split in both directions, so the whole block is strongly connected.
Thus the SCCs are exactly the blocks between positions where
Counting reachable pairs
Let SCC sizes from left to right be
Inside an SCC, all ordered pairs are reachable.
From a later SCC to an earlier SCC, all ordered pairs are reachable, because moving left is direct.
From an earlier SCC to a later SCC, nothing crosses the separating wall.
So the answer is
This can be rewritten as
So we only need to maintain
Then print
Turning cuts into covered gaps
For each position , draw the interval between and .
This interval crosses every gap between those two numbers. A gap is crossed by some interval iff the first positions do not contain exactly the first values.
Therefore:
Now a query swaps two values of , say positions and .
Only two intervals change:
So each query is just four range additions on gap cover counts.
Maintaining zero gaps with sqrt decomposition
There are gaps. Split them into blocks of about gaps.
For every gap we store its cover count, with lazy addition per block.
For every block, we also store summaries of positions grouped by their stored count. The only group we need during answer calculation is the group whose actual count is zero.
If a block has lazy value lz, then a stored count v has actual count
So zero gaps in that block are exactly the stored-count group
For that zero group, store:
Then we can recompute by scanning the blocks left to right. If the zero cuts are
then
The block summary lets us add a whole block's zero cuts in .
Range add:
This gives time. The implementation also keeps a compact block lookup table so rebuilding stays deterministic and fast enough for the 2-second nonsense.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
static inline ll sq(ll x) { return x * x; }
struct SqrtGaps {
struct Group {
int val, first, last;
ll inside;
};
int n, m, B, nb, W;
vector<int> cnt, bel, L, R, lazy;
vector<int16_t> where;
vector<vector<Group>> groups;
SqrtGaps() {}
SqrtGaps(int n_, const vector<int>& a) {
init(n_, a);
}
int getId(int b, int v) const {
return where[(size_t)b * W + v];
}
void setId(int b, int v, int id) {
where[(size_t)b * W + v] = (int16_t)id;
}
void init(int n_, const vector<int>& a) {
n = n_;
m = n - 1;
B = 320;
nb = (m + B - 1) / B;
W = n + 1;
cnt.assign(m + 1, 0);
vector<int> diff(m + 2, 0);
for (int i = 1; i <= n; i++) {
int l = min(i, a[i]);
int r = max(i, a[i]) - 1;
if (l <= r) {
diff[l]++;
diff[r + 1]--;
}
}
for (int i = 1, cur = 0; i <= m; i++) {
cur += diff[i];
cnt[i] = cur;
}
bel.assign(m + 1, 0);
L.resize(nb);
R.resize(nb);
lazy.assign(nb, 0);
groups.assign(nb, {});
where.assign((size_t)nb * W, (int16_t)-1);
for (int b = 0; b < nb; b++) {
L[b] = b * B + 1;
R[b] = min(m, (b + 1) * B);
for (int i = L[b]; i <= R[b]; i++) bel[i] = b;
rebuild(b);
}
}
void materialize(int b) {
if (lazy[b] == 0) return;
for (int i = L[b]; i <= R[b]; i++) cnt[i] += lazy[b];
lazy[b] = 0;
}
void rebuild(int b) {
for (auto &g : groups[b]) setId(b, g.val, -1);
groups[b].clear();
for (int i = L[b]; i <= R[b]; i++) {
int v = cnt[i];
int id = getId(b, v);
if (id == -1) {
id = (int)groups[b].size();
setId(b, v, id);
groups[b].push_back({v, i, i, 0});
} else {
Group &g = groups[b][id];
g.inside += sq(i - g.last);
g.last = i;
}
}
}
void rangeAdd(int l, int r, int delta) {
if (l > r || m == 0) return;
int bl = bel[l], br = bel[r];
if (bl == br) {
materialize(bl);
for (int i = l; i <= r; i++) cnt[i] += delta;
rebuild(bl);
return;
}
materialize(bl);
for (int i = l; i <= R[bl]; i++) cnt[i] += delta;
rebuild(bl);
materialize(br);
for (int i = L[br]; i <= r; i++) cnt[i] += delta;
rebuild(br);
for (int b = bl + 1; b < br; b++) lazy[b] += delta;
}
void addInterval(int x, int y, int delta) {
if (x == y) return;
if (x > y) swap(x, y);
rangeAdd(x, y - 1, delta);
}
ll sumSquares() const {
ll ans = 0;
int prev = 0;
for (int b = 0; b < nb; b++) {
int need = -lazy[b];
if (need < 0 || need > n) continue;
int id = getId(b, need);
if (id == -1) continue;
const Group &g = groups[b][id];
ans += sq(g.first - prev) + g.inside;
prev = g.last;
}
ans += sq(n - prev);
return ans;
}
};
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, q;
cin >> n >> q;
vector<int> p(n + 1), s(n + 1), a(n + 1);
for (int i = 1; i <= n; i++) cin >> p[i];
for (int i = 1; i <= n; i++) cin >> s[i];
for (int i = 1; i <= n; i++) a[p[i]] = s[i];
SqrtGaps ds(n, a);
while (q--) {
int tp, i, j;
cin >> tp >> i >> j;
int x = p[i], y = p[j];
int ax = a[x], ay = a[y];
ds.addInterval(x, ax, -1);
ds.addInterval(y, ay, -1);
ds.addInterval(x, ay, +1);
ds.addInterval(y, ax, +1);
swap(a[x], a[y]);
if (tp == 1) swap(p[i], p[j]);
else swap(s[i], s[j]);
ll sccSquares = ds.sumSquares();
cout << (sq(n) + sccSquares) / 2 << '\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 inline ll sq(ll x) { return x * x; }
struct SqrtGaps {
struct Group {
int val, first, last;
ll inside;
};
int n, m, B, nb, W;
vector<int> cnt, bel, L, R, lazy;
vector<int16_t> where;
vector<vector<Group>> groups;
SqrtGaps() {}
SqrtGaps(int n_, const vector<int>& a) {
init(n_, a);
}
int getId(int b, int v) const {
return where[(size_t)b * W + v];
}
void setId(int b, int v, int id) {
where[(size_t)b * W + v] = (int16_t)id;
}
void init(int n_, const vector<int>& a) {
n = n_;
m = n - 1;
B = 320;
nb = (m + B - 1) / B;
W = n + 1;
cnt.assign(m + 1, 0);
vector<int> diff(m + 2, 0);
for (int i = 1; i <= n; i++) {
int l = min(i, a[i]);
int r = max(i, a[i]) - 1;
if (l <= r) {
diff[l]++;
diff[r + 1]--;
}
}
for (int i = 1, cur = 0; i <= m; i++) {
cur += diff[i];
cnt[i] = cur;
}
bel.assign(m + 1, 0);
L.resize(nb);
R.resize(nb);
lazy.assign(nb, 0);
groups.assign(nb, {});
where.assign((size_t)nb * W, (int16_t)-1);
for (int b = 0; b < nb; b++) {
L[b] = b * B + 1;
R[b] = min(m, (b + 1) * B);
for (int i = L[b]; i <= R[b]; i++) bel[i] = b;
rebuild(b);
}
}
void materialize(int b) {
if (lazy[b] == 0) return;
for (int i = L[b]; i <= R[b]; i++) cnt[i] += lazy[b];
lazy[b] = 0;
}
void rebuild(int b) {
for (auto &g : groups[b]) setId(b, g.val, -1);
groups[b].clear();
for (int i = L[b]; i <= R[b]; i++) {
int v = cnt[i];
int id = getId(b, v);
if (id == -1) {
id = (int)groups[b].size();
setId(b, v, id);
groups[b].push_back({v, i, i, 0});
} else {
Group &g = groups[b][id];
g.inside += sq(i - g.last);
g.last = i;
}
}
}
void rangeAdd(int l, int r, int delta) {
if (l > r || m == 0) return;
int bl = bel[l], br = bel[r];
if (bl == br) {
materialize(bl);
for (int i = l; i <= r; i++) cnt[i] += delta;
rebuild(bl);
return;
}
materialize(bl);
for (int i = l; i <= R[bl]; i++) cnt[i] += delta;
rebuild(bl);
materialize(br);
for (int i = L[br]; i <= r; i++) cnt[i] += delta;
rebuild(br);
for (int b = bl + 1; b < br; b++) lazy[b] += delta;
}
void addInterval(int x, int y, int delta) {
if (x == y) return;
if (x > y) swap(x, y);
rangeAdd(x, y - 1, delta);
}
ll sumSquares() const {
ll ans = 0;
int prev = 0;
for (int b = 0; b < nb; b++) {
int need = -lazy[b];
if (need < 0 || need > n) continue;
int id = getId(b, need);
if (id == -1) continue;
const Group &g = groups[b][id];
ans += sq(g.first - prev) + g.inside;
prev = g.last;
}
ans += sq(n - prev);
return ans;
}
};
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, q;
cin >> n >> q;
vector<int> p(n + 1), s(n + 1), a(n + 1);
for (int i = 1; i <= n; i++) cin >> p[i];
for (int i = 1; i <= n; i++) cin >> s[i];
for (int i = 1; i <= n; i++) a[p[i]] = s[i];
SqrtGaps ds(n, a);
while (q--) {
int tp, i, j;
cin >> tp >> i >> j;
int x = p[i], y = p[j];
int ax = a[x], ay = a[y];
ds.addInterval(x, ax, -1);
ds.addInterval(y, ay, -1);
ds.addInterval(x, ay, +1);
ds.addInterval(y, ax, +1);
swap(a[x], a[y]);
if (tp == 1) swap(p[i], p[j]);
else swap(s[i], s[j]);
ll sccSquares = ds.sumSquares();
cout << (sq(n) + sccSquares) / 2 << '\n';
}
}
return 0;
}