Official Analysis (C++)

Explanation

If you've done Wormhole Sort, this problem should look a bit familiar. From that problem's editorial, we know that it's possible to sort a component iff each positions' sorted position is in the same component as itself. This motivates a DSU-esque solution (for more info look at the Guide solution for Wormhole Sort).

Hashing

The main issue is addressing type 3 and type 4 queries. We need to be able to efficiently check if a component can be sorted, and if it can't, we also need to see how many components it can be paired up with so that they can be sorted as a whole.

To do this, we have to check if a component has all the needed numbers for it to be able to be rearranged into sorted order.

For example, say this was our (0-indexed) array:

[16,2,10,7,12,5,3,7,20,11][16, 2, 10, 7, 12, 5, 3, 7, 20, 11]

A component that consisted of the indexes 00, 33, and 44 would need the numbers 22, 77, and 77 to be sortable.

We can get all the needed numbers by sorting the given array and getting the numbers at the positions of the components, but how can we quickly check if the two sets of numbers are the same? The answer is hashing.

If we assign each number a certain random number, we can obtain our hash by multiplying each random number by the number of times it occurs in the set.

For example, if our component was the multiset 3,3,4,4,5{3, 3, 4, 4, 5}, and RiR_i would be the random number of ii, our hash would be:

2R3+2R4+1R52 \cdot R_3 + 2 \cdot R_4 + 1 \cdot R_5

It's also possible to assign each distinct number in the array a distinct power of a certain random number. However, this might result in a higher chance of hashes colliding.

Each component should not only have a hash of its number, but also a hash of the numbers it's supposed to have. For example, if our component was just the number 33 but needs a 44 to be sorted, its hash and required hash would be R3R_3 and R4R_4 respectively. Having this "required hash" allows us to quickly check if each component is sortable or not.

When executing a union operation (aka a type 1 query), we can add the hashes and required hashes of the two components to get the hash of the combined component. When swapping two array elements (a type 2 query), we add and subtract the swapped elements' random values from each component, while leaving the required hashes untouched.

Tracking "Bad" Components

However, when updating components, we also have to keep track of all the (henceforth referred to as "bad") components in the array to answer the output queries.

First, we need the number of bad components after each modification. To answer a type 3 query, we check if there's any bad components. If there are, we output NE, and otherwise, we output DA.

However, answering type 4 queries are a bit more tricky. We need some way to match up components such that the sum of their required hashes equals the sum of their actual hashes. In other words, if we let xx be the actual hash and yy be the required hash, we need to find all pairs of components (ii and jj) such that the following is satisfied:

xi+xj=yi+yjx_i+x_j=y_i+y_j

If we put the iis on one side, and jjs on another, we get this:

xiyi=yjxjx_i-y_i=y_j-x_j

This motivates us keeping a map of the difference between the required hash and actual hash and the number of positions whose components have such a difference. It doesn't matter whether we subtract the required hash from the actual hash or vice versa, as long as we stay consistent. We also keep track of the number of pairs of bad components that can be matched up.

When adding or removing a bad component, we get its difference dd and see how many positions whose components have the difference d-d through the map to update the number of pairs.

Implementation

Time Complexity: O(Qα(N))\mathcal{O}(Q \cdot \alpha(N))

Chance of Collision: O(N2MOD1MOD2)\mathcal{O}\left(\frac{N^2}{MOD_1 * MOD_2}\right)

C++

#include <algorithm>
#include <iostream>
#include <random>
#include <unordered_map>
#include <vector>
using std::cout;
using std::endl;
using std::vector;
using ll = long long;

Join the USACO Forum!

Stuck on a problem, or don't understand a module? Join the USACO Forum and get help from other competitive programmers!