New Year's Puzzle (Codeforces 1472F)
Tags: Greedy, graph matchings

Every year Santa Claus gives gifts to all children. However, each country has its own traditions, and this process takes place in different ways. For example, in Berland you need to solve the New Year's puzzle.Polycarp got the following problem: given a grid strip of size $$$2 \times n$$$, some cells of it are blocked. You need to check if it is possible to tile all free cells using the $$$2 \times 1$$$ and $$$1 \times 2$$$ tiles (dominoes).For example, if $$$n = 5$$$ and the strip looks like this (black cells are blocked):  Then it can be tiled, for example, using two vertical and two horizontal tiles, as in the picture below (different tiles are marked by different colors).  And if $$$n = 3$$$ and the strip looks like this:  It is impossible to tile free cells.Polycarp easily solved this task and received his New Year's gift. Can you solve it?

Input: The first line contains an integer $$$t$$$ ($$$1 \leq t \leq 10^4$$$) — the number of test cases. Then $$$t$$$ test cases follow.Each test case is preceded by an empty line.The first line of each test case contains two integers $$$n$$$ and $$$m$$$ ($$$1 \le n \le 10^9$$$, $$$1 \le m \le 2 \cdot 10^5$$$) — the length of the strip and the number of blocked cells on it.Each of the next $$$m$$$ lines contains two integers $$$r_i, c_i$$$ ($$$1 \le r_i \le 2, 1 \le c_i \le n$$$) — numbers of rows and columns of blocked cells. It is guaranteed that all blocked cells are different, i.e. $$$(r_i, c_i) \ne (r_j, c_j), i \ne j$$$.It is guaranteed that the sum of $$$m$$$ over all test cases does not exceed $$$2 \cdot 10^5$$$.

Output: For each test case, print on a separate line:   "YES", if it is possible to tile all unblocked squares with the $$$2 \times 1$$$ and $$$1 \times 2$$$ tiles;  "NO" otherwise. You can output "YES" and "NO" in any case (for example, the strings yEs, yes, Yes and YES will be recognized as positive).

Note: The first two test cases are explained in the statement.In the third test case the strip looks like this:    It is easy to check that the unblocked squares on it can not be tiled.

Sample Inputs:
3

5 2
2 2
1 4

3 2
2 1
2 3

6 4
2 1
2 3
2 4
2 6

Sample Outputs:
YES
NO
NO

Solution (Python):
""""""
__version__ = '1.0'
__date__ = '2021-03-19'
import sys
from operator import itemgetter

def solve(n, m, obstacles):
    """"""
    if len(obstacles) % 2 == 1:
        return 'NO'
    obstacles.sort(key=itemgetter(0))
    curr = 0
    while curr < m:
        (c1, r1) = obstacles[curr]
        (c2, r2) = obstacles[curr + 1]
        if c1 == c2:
            curr += 2
            continue
        if curr + 2 < m:
            (c3, r3) = obstacles[curr + 2]
            if c2 == c3:
                return 'NO'
        distance = c2 - c1 + r2 - r1
        if distance % 2 == 0:
            return 'NO'
        curr += 2
    return 'YES'

def main(argv=None):
    t = int(input())
    for _ in range(t):
        input()
        (n, m) = map(int, input().split())
        obstacles = list()
        for i in range(m):
            (r, c) = map(int, input().split())
            obstacles.append((c - 1, r - 1))
        print(solve(n, m, obstacles))
    return 0
if __name__ == '__main__':
    STATUS = main()
    sys.exit(STATUS)

Solution (Python):
import sys
z = sys.stdin.readline
for _ in [8] * int(z()):
    z()
    (_, m) = map(int, z().split())
    d = l = 0
    c = [[*map(int, z().split())] for _ in range(m)]
    c.sort(key=lambda x: x[1])
    for (x, y) in c:
        if abs((d := (d + (x + y) % 2 * 2 - 1))) > 1 or (l == y and d):
            print('no')
            break
        l = y
    else:
        print('no' if d else 'yes')

Solution (Python):
in_length = int(input())
final_ouput = []
for i in range(in_length):
    blank = input()
    [length, num_obstructions] = input().split()
    state_saver = dict()
    h_positon_obstacles = []
    for j in range(int(num_obstructions)):
        [row, column] = input().split()
        col_index = int(column) - 1
        if not col_index in state_saver:
            state_saver.update({col_index: row})
        else:
            state_saver.update({col_index: state_saver[col_index] + row})
        h_positon_obstacles.append(col_index)
    final_ouput.append('YES')
    if len(h_positon_obstacles) % 2 != 0:
        final_ouput[i] = 'NO'
        continue
    h_positon_obstacles = sorted(h_positon_obstacles)
    for j in range(0, len(h_positon_obstacles), 2):
        block1 = h_positon_obstacles[j]
        block2 = h_positon_obstacles[j + 1]
        if block1 == block2:
            continue
        col_block1 = state_saver[block1]
        col_block2 = state_saver[block2]
        if len(col_block1) == len(col_block2):
            separation_parity = (block2 - block1 - 1) % 2
            if col_block1 != col_block2 and separation_parity == 0:
                final_ouput[i] = 'NO'
                break
            elif col_block1 == col_block2 and separation_parity != 0:
                final_ouput[i] = 'NO'
                break
        else:
            final_ouput[i] = 'NO'
            break
for out in final_ouput:
    print(out)