POI - Cards

Authors: Dong Liu, Dustin Miao

Time Complexity: O(NlogN)\mathcal O(N\log N)

Explanation

We can use a segment tree to solve this problem.

For each segment tree node with the range [l,r][l, r], we will store if it's possible to arrange the cards lrl\dots r so that we either

  • don't flip card ll and card rr
  • don't flip card ll and flip card rr
  • flip card ll and don't flip card rr
  • flip card ll and card rr

We can represent these states in a 2 by 2 matrix bool[2][2] where the first dimension stores whether we flip card ll and the second dimension stores whether we flip card rr.

To calculate the states for a specific segment tree node, we loop through all possible states of our left child node (a,b)(a, b) and all possible states of our right child node (c,d)(c, d). If the transition from bb to cc works, then (a,d)(a, d) works since we're only considering endpoints.

To perform a swap operation with index ii and jj, we simply swap the cards at ii and jj. We can just recalculate all nodes in the segment tree that are related to either ii or jj. Since there are around O(logN)\mathcal O(\log N) ranges in the segment tree that contain ii or jj, this whole swap operation takes O(logN)\mathcal O(\log N).

Additionally, we also have to print if we can form a non-decreasing list using the cards. To do this, if any states of the root node in our segment tree (which covers [1,n][1, n]) is possible, then our answer is yes (TAK), otherwise no (NIE).

Implementation

C++

#include <bits/stdc++.h>
using namespace std;
const int N = 1 << 18;
int n, q, aa[N][2];
bool tt[N << 1][2][2];
void pull(int i, int l, int r) {
memset(tt[i], 0, sizeof(tt[i]));

Alternate Solution

We can reduce the number of states stored in the segment tree to just two:

  • The minimum last value of the interval given that we take the lesser side of the first card (denote this as FFFF)
  • The minimum last value of the interval given that we take the greater side of the first card (denote this as SSSS)

Note that it will always be optimal to take the lesser side of the last side given that we can take both. This, in turn, prepares the current interval with the next merging step, if applicable.

Let's denote the array of cards as AA, where A.FFA.FF is the front side and A.SSA.SS is the back side. Without loss of generality, assume that A[i].FFA[i].SSA[i].FF \leq A[i].SS for all i[1,n]i \in [1, n]. We consider merging interval uu and vv to create interval ww, where ww corresponds to the array elements [l,r][l, r]. Note that this implies uu corresponds to [l,m][l, m] and vv corresponds to [m+1,r][m + 1, r], where m=l+r2m = \lfloor \frac {l + r} 2 \rfloor

  • w.FF={v.FFu.FFA[m+1].FFv.SSA[tm+1].FF<u.FFA[tm+1].SSotherwisew.FF = \begin{cases} v.FF & u.FF \leq A[m + 1].FF \\ v.SS & A[tm + 1].FF < u.FF \leq A[tm + 1].SS \\ \infty & \text{otherwise}\end{cases}
  • w.SS={v.FFu.SSA[m+1].FFv.SSA[tm+1].FF<u.SSA[tm+1].SSotherwisew.SS = \begin{cases} v.FF & u.SS \leq A[m + 1].FF \\ v.SS & A[tm + 1].FF < u.SS \leq A[tm + 1].SS \\ \infty & \text{otherwise}\end{cases}

Note that we choose \infty to be the default value, because under our merge conditions, infinity conveniently overrides all other operators. This means that if any point of the array is discontinuous (or unable to be made into a non-decreasing sequence), node 11 in the segment tree, which corresponds to the entire array, will also have w.FF=w.SS=w.FF = w.SS = \infty.

Time Complexity: O(NlogN)\mathcal{O}(N\log{N})

Implementation

C++

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pll = pair<ll, ll>;
#define MP make_pair
#define FF first
#define SS second

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!