Tokens on Graph (Codeforces 1790G)
Tags: Others, shortest paths

You are given an undirected connected graph, some vertices of which contain tokens and/or bonuses. Consider a game involving one player  — you.You can move tokens according to the following rules:  At the beginning of the game, you can make exactly one turn: move any token to any adjacent vertex.  If the movement of the token ended on the bonus, then you are allowed to make another turn with any other token. You can use different bonuses in any order. The same bonus can be used an unlimited number of times. Bonuses do not move during the game.There can be several tokens in one vertex at the same time, but initially there is no more than one token in each vertex.The vertex with number $$$1$$$ is the finish vertex, and your task is to determine whether it is possible to hit it with any token by making turns with the tiles according to the rules described above. If a token is initially located at the vertex of $$$1$$$, then the game is considered already won.  The finish line is in black, the bonuses are in red, the chips are in grey.  For example, for a given graph, you can reach the finish line with a chip from the $$$8$$$th vertex by making the following sequence of turns:   Move from the $$$8$$$-th vertex to the $$$6$$$-th.  Move from the $$$7$$$-th vertex to the $$$5$$$-th.  Move from the $$$6$$$-th vertex to the $$$4$$$-th.  Move from the $$$5$$$-th vertex to the $$$6$$$-th.  Move from the $$$4$$$-th vertex to the $$$2$$$-nd.  Move from the $$$6$$$-th vertex to the $$$4$$$-th.  Move from the $$$2$$$-nd vertex to the $$$1$$$-st vertex, which is the finish. 

Input: The first line of input data contains a single integer $$$t$$$ ($$$1 \le t \le 10^4$$$) — number of test cases in the test. The descriptions of the test cases follow.The first line of the description of each test case contains two integers $$$n$$$ and $$$m$$$ ($$$1 \le n \le 2 \cdot 10^5$$$, $$$0 \le m \le 2 \cdot 10^5$$$) — the number of vertices and edges in the graph, respectively.The second line of the description of each test case contains two integers $$$p$$$ and $$$b$$$ ($$$1 \le p \le n, 0 \le b \le n$$$) — the number of tokens and bonuses, respectively.The third line of the description of each test case contains $$$p$$$ different integers from $$$1$$$ to $$$n$$$ — the indices of the vertices in which the tokens are located.The fourth line of the description of each input data set contains $$$b$$$ different integers from $$$1$$$ to $$$n$$$ — the indices of the vertices in which the bonuses are located. Note that the value of $$$b$$$ can be equal to $$$0$$$. In this case, this line is empty.There can be both a token and a bonus in one vertex at the same time.The next $$$m$$$ lines of the description of each test case contain two integers $$$u_i$$$ and $$$v_i$$$ ($$$1 \le u_i, v_i \le n$$$, $$$u_i \ne v_i$$$) — vertices connected by the $$$i$$$-th edge. There is at most one edge between each pair of vertices. The given graph is connected, that is, from any vertex you can get to any one by moving along the edges.The test cases are separated by an empty string.It is guaranteed that the sum of $$$n$$$ over all test cases does not exceed $$$2 \cdot 10^5$$$. Similarly, it is guaranteed that the sum of $$$m$$$ over all input data sets does not exceed $$$2 \cdot 10^5$$$.

Output: For each test case, print YES in a separate line if you can reach the finish with some token, and NO otherwise.You can output YES and NO in any case (for example, the strings yEs, yes, Yes and YES will be recognized as a positive response).

Note:   The first test case is explained in the statement.  In the second test case, there is only one token which can make only one turn, and it cannot reach the finish.  In the third test case, the token can reach the finish line in $$$1$$$ turn.  In the fourth test case, you need to make just one turn from $$$2$$$ to $$$1$$$.  In the sixth test case, the token is initially at node number $$$1$$$, so we win immediately. 

Sample Inputs:
6
8 10
2 4
7 8
2 4 5 6
1 2
2 3
2 4
3 4
3 5
4 6
5 6
5 7
6 8
7 8
5 4
1 1
5
3
1 2
2 3
3 4
4 5
2 1
1 0
2
1 2
4 3
1 2
2
3 4
1 2
2 3
2 4
5 4
3 2
5 3 4
2 4
1 2
2 3
3 4
4 5
1 0
1 1
1
1

Sample Outputs:
YES
NO
YES
YES
YES
YES

Solution (Python):
import sys
input = sys.stdin.buffer.readline
from collections import deque
tc = int(input())
for _ in range(tc):
    (n, m) = map(int, input().split())
    (p, b) = map(int, input().split())
    t = list(map(int, input().split()))
    bo = list(map(int, input().split()))
    adj = [[] for _ in range(n + 1)]
    for _ in range(m):
        (u, v) = map(int, input().split())
        adj[u].append(v)
        adj[v].append(u)
    if _ != tc - 1:
        _ = input()
    has_bo = [0] * (n + 1)
    for i in bo:
        has_bo[i] = 1
    q = deque([1])
    d = [-1] * (n + 1)
    d[1] = 0
    while q:
        c = q.popleft()
        for ne in adj[c]:
            if d[ne] == -1:
                d[ne] = d[c] + 1
                if has_bo[ne]:
                    q.append(ne)
    ans = False
    token1 = -1
    for i in t:
        if ~d[i]:
            if ~token1:
                ans = True
                break
            else:
                token1 = i
    if ans:
        print('YES')
    elif ~token1:
        nei_bo = [0] * (n + 1)
        for i in range(1, n + 1):
            for j in adj[i]:
                nei_bo[i] |= has_bo[j]
        moves = 1
        for x in t:
            if x != token1:
                largest = 0
                for y in adj[x]:
                    if has_bo[y]:
                        if nei_bo[y]:
                            ans = True
                        else:
                            largest = 1
                moves += largest
        print('YES' if ans or moves >= d[token1] else 'NO')
    else:
        print('NO')

Solution (Python):
import sys, os, io
input = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline

def bfs(s):
    (q, k) = ([s], 0)
    dist = [inf] * (n + 1)
    dist[s] = 0
    while len(q) ^ k:
        i = q[k]
        if not y[i]:
            k += 1
            continue
        di = dist[i]
        for j in G[i]:
            if dist[j] == inf and (x[j] or y[j]):
                dist[j] = di + 1
                q.append(j)
        k += 1
    return dist
t = int(input())
ans = []
inf = pow(10, 9) + 1
for t0 in range(t):
    (n, m) = map(int, input().split())
    (p, b) = map(int, input().split())
    (x, y) = ([0] * (n + 1), [0] * (n + 1))
    p0 = list(map(int, input().split()))
    for i in p0:
        x[i] = 1
    b0 = list(map(int, input().split()))
    for i in b0:
        y[i] = 1
    y[1] = 1
    G = [[] for _ in range(n + 1)]
    for _ in range(m):
        (u, v) = map(int, input().split())
        G[u].append(v)
        G[v].append(u)
    dist = bfs(1)
    (ok, c) = (0, 0)
    for i in p0:
        if dist[i] <= 1:
            ok = 1
        if dist[i] ^ inf:
            c += 1
    if c >= 2:
        ok = 1
    if not ok and c == 1:
        z = [0] * (n + 1)
        for i in range(1, n + 1):
            for j in G[i]:
                if y[j]:
                    z[i] = 1
                    break
        (f, v) = (0, 0)
        for i in p0:
            if dist[i] ^ inf:
                u = dist[i]
                continue
            f0 = 0
            for j in G[i]:
                if y[j] and z[j]:
                    f = 1
                elif y[j]:
                    f0 = 1
            if f:
                ok = 1
                break
            if f0:
                v += 1
        if u - 1 <= v:
            ok = 1
    ans0 = 'YES' if ok else 'NO'
    ans.append(ans0)
    if t0 < t - 1:
        input()
sys.stdout.write('\n'.join(ans))

Solution (Python):
import io, os
from collections import deque
input = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline

def unionfind(group, root):
    son = root
    while group[root] != root:
        root = group[root]
    while son != root:
        temp = group[son]
        group[son] = root
        son = group[temp]
    return root

def main(t):
    (n, m) = map(int, input().split())
    (p, b) = map(int, input().split())
    tokens = list(map(int, input().split()))
    if b > 0:
        bonus = list(map(int, input().split()))
    else:
        s = input()
        bonus = []
    for i in range(p):
        tokens[i] -= 1
    for i in range(b):
        bonus[i] -= 1
    bonus = set(bonus)
    bonus.add(0)
    group = [i for i in range(n)]
    groupnum = [0 for i in range(n)]
    neigh = [[] for _ in range(n)]
    distance = [float('inf')] * n
    distance[0] = 0
    for i in range(m):
        (u, v) = map(int, input().split())
        neigh[u - 1].append(v - 1)
        neigh[v - 1].append(u - 1)
        if u - 1 in bonus and v - 1 in bonus:
            rootu = unionfind(group, u - 1)
            rootv = unionfind(group, v - 1)
            group[max(rootu, rootv)] = min(rootu, rootv)
    s = input()
    if 0 in tokens:
        print('YES')
        return
    for i in range(n):
        group[i] = unionfind(group, i)
        groupnum[group[i]] += 1
    queue = deque()
    queue.append([0, 0])
    while queue:
        (index, d) = queue.popleft()
        for nextindex in neigh[index]:
            if distance[nextindex] < float('inf'):
                continue
            if group[nextindex] != 0:
                continue
            distance[nextindex] = d + 1
            queue.append([nextindex, d + 1])
    toend = [float('inf')] * p
    move = [0] * p
    moveleft = [0] * (p + 1)
    moveright = [0] * (p + 1)
    for i in range(p):
        index = tokens[i]
        for nextindex in neigh[index]:
            toend[i] = min(distance[nextindex], toend[i])
            if nextindex in bonus:
                move[i] = max(move[i], groupnum[group[nextindex]])
        if move[i] > 1:
            move[i] = float('inf')
        moveleft[i] = moveleft[i - 1] + move[i]
    for i in range(p - 1, -1, -1):
        moveright[i] = moveright[i + 1] + move[i]
    for i in range(p):
        rest = moveleft[i - 1] + moveright[i + 1]
        if rest >= toend[i] and toend[i] < float('inf'):
            print('YES')
            return
    print('NO')
T = int(input())
t = 1
while t <= T:
    main(t)
    t += 1