Cheap Dinner (Codeforces 1487E)
Tags: Greedy, two pointers

Ivan wants to have a good dinner. A good dinner should consist of a first course, a second course, a drink, and a dessert.There are $$$n_1$$$ different types of first courses Ivan can buy (the $$$i$$$-th of them costs $$$a_i$$$ coins), $$$n_2$$$ different types of second courses (the $$$i$$$-th of them costs $$$b_i$$$ coins), $$$n_3$$$ different types of drinks (the $$$i$$$-th of them costs $$$c_i$$$ coins) and $$$n_4$$$ different types of desserts (the $$$i$$$-th of them costs $$$d_i$$$ coins).Some dishes don't go well with each other. There are $$$m_1$$$ pairs of first courses and second courses that don't go well with each other, $$$m_2$$$ pairs of second courses and drinks, and $$$m_3$$$ pairs of drinks and desserts that don't go well with each other.Ivan wants to buy exactly one first course, one second course, one drink, and one dessert so that they go well with each other, and the total cost of the dinner is the minimum possible. Help him to find the cheapest dinner option!

Input: The first line contains four integers $$$n_1$$$, $$$n_2$$$, $$$n_3$$$ and $$$n_4$$$ ($$$1 \le n_i \le 150000$$$) — the number of types of first courses, second courses, drinks and desserts, respectively.Then four lines follow. The first line contains $$$n_1$$$ integers $$$a_1, a_2, \dots, a_{n_1}$$$ ($$$1 \le a_i \le 10^8$$$), where $$$a_i$$$ is the cost of the $$$i$$$-th type of first course. Three next lines denote the costs of second courses, drinks, and desserts in the same way ($$$1 \le b_i, c_i, d_i \le 10^8$$$).The next line contains one integer $$$m_1$$$ ($$$0 \le m_1 \le 200000$$$) — the number of pairs of first and second courses that don't go well with each other. Each of the next $$$m_1$$$ lines contains two integers $$$x_i$$$ and $$$y_i$$$ ($$$1 \le x_i \le n_1$$$; $$$1 \le y_i \le n_2$$$) denoting that the first course number $$$x_i$$$ doesn't go well with the second course number $$$y_i$$$. All these pairs are different.The block of pairs of second dishes and drinks that don't go well with each other is given in the same format. The same for pairs of drinks and desserts that don't go well with each other ($$$0 \le m_2, m_3 \le 200000$$$).

Output: If it's impossible to choose a first course, a second course, a drink, and a dessert so that they go well with each other, print $$$-1$$$. Otherwise, print one integer — the minimum total cost of the dinner.

Note: The best option in the first example is to take the first course $$$2$$$, the second course $$$1$$$, the drink $$$2$$$ and the dessert $$$1$$$.In the second example, the only pair of the first course and the second course is bad, so it's impossible to have dinner.

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

1 1 1 1
1
1
1
1
1
1 1
0
0

Sample Outputs:
26

-1

Solution (Python):
from sys import stdin, stdout
import collections, heapq, bisect, math
input = stdin.readline

def rint():
    return int(input())

def rstr():
    return input().strip()

def rlstr():
    return list(input().strip().split())

def rlint():
    return list(map(int, input().split()))

def main():
    n = rlint()
    a = rlint()
    b = rlint()
    c = rlint()
    d = rlint()
    price = [a, b, c, d]
    inc = [{} for _ in range(3)]
    for i in range(3):
        m = rint()
        for _ in range(m):
            (x, y) = rlint()
            x -= 1
            y -= 1
            if x not in inc[i]:
                inc[i][x] = set([y])
            else:
                inc[i][x].add(y)
    dp = price[-1][:]
    for step in range(3, 0, -1):
        dp2 = dp[:]
        dp = [float('inf')] * n[step - 1]
        index = sorted(range(n[step]), key=lambda i: dp2[i])
        for i in range(n[step - 1]):
            ind = 0
            while i in inc[step - 1] and ind < n[step] and (index[ind] in inc[step - 1][i]):
                ind += 1
            if ind < n[step]:
                dp[i] = price[step - 1][i] + dp2[index[ind]]
    res = min(dp)
    if res < float('inf'):
        print(res)
    else:
        print(-1)
if __name__ == '__main__':
    main()

Solution (Python):
import sys
input = sys.stdin.readline

def main():
    n = list(map(int, input().split()))
    a = list(map(int, input().split()))
    b = list(map(int, input().split()))
    c = list(map(int, input().split()))
    d = list(map(int, input().split()))
    prices = [a, b, c, d]
    inc = [{} for _ in range(3)]
    for i in range(3):
        ni = int(input())
        for _ in range(ni):
            (k, l) = map(int, input().split())
            if k - 1 in inc[i]:
                inc[i][k - 1].add(l - 1)
            else:
                inc[i][k - 1] = set([l - 1])
    step = 3
    mem2 = prices[3][:]
    for step in range(3, 0, -1):
        mem = mem2[:]
        index_sorted = sorted(range(n[step]), key=lambda x: mem[x])
        mem2 = [float('inf')] * n[step - 1]
        for i in range(n[step - 1]):
            index = 0
            while i in inc[step - 1] and index < n[step] and (index_sorted[index] in inc[step - 1][i]):
                index += 1
            if index < n[step]:
                mem2[i] = prices[step - 1][i] + mem[index_sorted[index]]
    rep = min(mem2)
    if rep < float('inf'):
        print(rep)
    else:
        print(-1)
main()

Solution (Python):
def main():
    n = list(map(int, input().split()))
    a = list(map(int, input().split()))
    b = list(map(int, input().split()))
    c = list(map(int, input().split()))
    d = list(map(int, input().split()))
    prices = [a, b, c, d]
    inc = [{} for _ in range(3)]
    for i in range(3):
        ni = int(input())
        for _ in range(ni):
            (k, l) = map(int, input().split())
            if k - 1 in inc[i]:
                inc[i][k - 1].add(l - 1)
            else:
                inc[i][k - 1] = set([l - 1])
    step = 3
    mem2 = prices[3][:]
    for step in range(3, 0, -1):
        mem = mem2[:]
        index_sorted = sorted(range(n[step]), key=lambda x: mem[x])
        mem2 = [float('inf')] * n[step - 1]
        for i in range(n[step - 1]):
            index = 0
            while i in inc[step - 1] and index < n[step] and (index_sorted[index] in inc[step - 1][i]):
                index += 1
            if index < n[step]:
                mem2[i] = prices[step - 1][i] + mem[index_sorted[index]]
    rep = min(mem2)
    print(rep) if rep < float('inf') else print(-1)
main()