Самый очевидный вариант: сократить количество проходов по массиву.
Второй проход вам совершенно не нужен, т. к. уже на первом можно подсчитать количество "закрашенных" точек (если точка ещё не закрашена - закрашиваем её и увеличиваем счётчик, иначе переходим к следующей). Имея количество "закрашенных" точек и площадь холста рассчёт сводится к одному действию, а сложность алгоритма уменьшается в два раза.
Если я ничего не путаю, то получится как-то так
w,h = input().split()
w = int(w)
k = 0
h = int(h)
canvasArea = w * h
count = int(input())
arr = [[0]*h for i in range(w)]
for i in range(count):
square = [int(l) for l in input().split()]
for i in range(square[0],square[2]):
for j in range(square[1],square[3]):
if arr[i][j]==0:
k += 1
arr[i][j] = 1
print(canvasArea - k)
PS Маленький совет по поводу именования переменных: если вы когда-нибудь собираетесь заняться программированием за рамками олимпиадок, то вместо w,h,k и arr стоит использовать что-то типа width, height, filledCount, canvas. Так как в чуть более объемном коде будет значительно сложнее понять, что означают все эти однобуквенные переменные.