Problem D. Deadlock detector

Author:A. Klenin   Time limit:2 sec
Input file:input.txt   Memory limit:64 Mb
Output file:output.txt  


Resource sharing is an important problem in parallel programming. When parallel tasks must use common resource, such as input/output channel, they serialize their access by using locks.

Two basic locking primitives are provided:

After the execution of the last locking primitive of the task, all resources locked by that task are unlocked.

Locking guarantees that only a single task can access the resource at a time. However, carelessly written programs may still lead to some unfortunate situations. If task A has already locked resource P and tries to lock Q, while task B has already locked Q and tries to lock P, they will both wait endlessly. This condition is called "deadlock".

Your program must, given lock/unlock sequences executed by two parallel tasks while accessing M resources, determine whether a deadlock is possible between them.

Input file format

First line of input file contains integers N1 N2 M. Following lines contain N1 locking primitives called by the first task, then N2 primitives called by the second task, in the order of calling.

Each primitive is located on a separate line and consists of a string LOCK or UNLOCK followed by one or more spaces and a resource number r (1 ≤ r ≤ M).

Output file format

Output file must contain integers D1 D2, designating that deadlock can occur when the first task is trying to execute primitive number D1 (1 ≤ D1 ≤ N1) while the second task is trying to execute primitive number D2 (1 ≤ D2 ≤ N2).

If there is more than one possible deadlock, output the one with minimal D1. If there is still more then one, output the one with minimal D2. If the deadlock is not possible, output file must contain a single 0 (zero).


1 ≤ N1, N2 ≤ 5000, 1 ≤ M≤ 100.

Sample tests

No. Input file (input.txt) Output file (output.txt)
2 2 2
2 2
3 3 5


Let us define the state function S: Si, j, k = 1 if the task number i(i = 1, 2) has locked resource j(j = 1, M) after execution of primitive k(k = 1, Ni), and 0 otherwise. Additionally, let us define Si, j, 0 = 0. Values of S can easily be calculated either in a separate pass before main loop, or during the main loop as needed.

Deadlocks are obviously possible only between two locking primitives. We can analyze each pair of locking primitives p1(p1 = 1, N1) and p2(p2 = 1, N2), trying to lock resources r1 and r2 correspondingly. The condition of deadlock, derived straight from definition, is: S1, r2, p1 − 1 = 1 and S2, r1, p2 − 1 = 1.

Deadlock can be actually realized if pair of primitives p1, p2 can be executed simultaneously. This is possible in all cases except two.

Firstly, both primitives can be protected by common lock as shown in the second sample test. This condition can be formalized: there exists such r that S1, r, p1 − 1 = 1 and S2, r, p2 − 1 = 1.

Secondly, there may be a deadlock earlier which prevents execution from reaching pair p1, p2. Since the problem requires to output earliest of deadlocks, this case can be preventing if we check values of p1 and p2 in ascending order and stop after the first deadlock found.

This algorithms solves the problem using O(N1 N2 M) operations. The most expensive part of solution is checking for common locks. It can be performed in constant time if we notice that only one bit of state function can change on each step. This reduces complexity to O(N1 N2).

0.080s 0.012s 13