Buggy python submission (verdict: Time Limit Exceeded):
# 後で１の位から処理するためにひっくり返しておく
S = list(map(int, reversed(input())))
#print("S = ", S)
"""
「2019の倍数か」は「mod 2019 が 0 になるか」と同意。
しかし、普通に切り出して、１つずつチェックすると計算工数がかかるので、累積和を使う。
各桁の値を （桁の数値a_k）× 10^k と表現し、mod 2019 の値を予め計算し、累積和を作っておく。
ここで、10^k の部分は、2019と互いに疎（公約数がない）なので、
含めて計算しても mod 2019 の周期を乱さない。（勿論オフセットは変わる）
「(i, j) が2019で割り切るか」は、「i～j の累積和が同じ値になる（2019周期）か」と同意。
累積和が同じ値になる個数を数えて、組み合わせを計算すればよい。
"""
# a　：　各桁の mod 2019 値（桁の数値 a_k × 10^k mod 2019）
a = [0 for i in range(len(S))]
# sums ：　累積和
sums = [0 for i in range(len(S) + 1)]
# d : 10^k
d = 1
# cnt : 累積和 mod 2019 の出現度数表
cnt = [0 for i in range(2019)]
cnt[0] = 1  # sums[0] = 0 の分
for i in range(len(S)):
    a[i] = (S[i] * d) % 2019
    sums[i + 1] = (sums[i] + a[i]) % 2019
    d *= 10
    cnt[sums[i + 1]] += 1
#print("sums = ", sums)
#print("cnt = ", cnt)
ans = 0
for i in cnt:
    ans += i * (i - 1) // 2
print(ans)


Fixed submission:
# 後で１の位から処理するためにひっくり返しておく
S = list(map(int, reversed(input())))
#print("S = ", S)
"""
「2019の倍数か」は「mod 2019 が 0 になるか」と同意。
しかし、普通に切り出して、１つずつチェックすると計算工数がかかるので、累積和を使う。
各桁の値を （桁の数値a_k）× 10^k と表現し、mod 2019 の値を予め計算し、累積和を作っておく。
ここで、10^k の部分は、2019と互いに疎（公約数がない）なので、
含めて計算しても mod 2019 の周期を乱さない。（勿論オフセットは変わる）
「(i, j) が2019で割り切るか」は、「i～j の累積和が同じ値になる（2019周期）か」と同意。
累積和が同じ値になる個数を数えて、組み合わせを計算すればよい。
"""
# a　：　各桁の mod 2019 値（桁の数値 a_k × 10^k mod 2019）
a = [0] * len(S)
# sums ：　累積和
sums = [0] * (len(S) + 1)
# d : 10^k
d = 1
# cnt : 累積和 mod 2019 の出現度数表
cnt = [0] * 2019
cnt[0] = 1  # sums[0] = 0 の分
for i in range(len(S)):
    a[i] = (S[i] * d) % 2019
    sums[i + 1] = (sums[i] + a[i]) % 2019
    d = (d * 10) % 2019 
    cnt[sums[i + 1]] += 1
#print("sums = ", sums)
#print("cnt = ", cnt)
ans = 0
for i in cnt:
    ans += i * (i - 1) // 2
print(ans)

Buggy python submission (verdict: Time Limit Exceeded):
import math
# 後で１の位から処理するためにひっくり返しておく
S = list(map(int, reversed(input())))
#print("S = ", S)
"""
「2019の倍数か」は「mod 2019 が 0 になるか」と同意。
しかし、普通に切り出して、１つずつチェックすると計算工数がかかるので、累積和を使う。
各桁の値を （桁の数値a_k）× 10^k と表現し、mod 2019 の値を予め計算し、累積和を作っておく。
ここで、10^k の部分は、2019と互いに疎（公約数がない）なので、
含めて計算しても mod 2019 の周期を乱さない。（勿論オフセットは変わる）
「(i, j) が2019で割り切るか」は、「i～j の累積和が同じ値になる（2019周期）か」と同意。
累積和が同じ値になる個数を数えて、組み合わせを計算すればよい。
"""
# a　：　各桁の mod 2019 値（桁の数値 a_k × 10^k mod 2019）
a = [0 for i in range(len(S))]
# sums ：　累積和
sums = [0 for i in range(len(S) + 1)]
for i in range(len(S)):
    a[i] = (S[i] * 10**i) % 2019
    sums[i + 1] = (sums[i] + a[i]) % 2019
#print("sums = ", sums)
sorts = sorted(sums)
cnt = 0
while len(sorts) > 0:
    picknum = len([i for i in sorts if i == sorts[0]])
    sorts = [i for i in sorts if i != sorts[0]]
    if picknum > 1:
        cnt += math.factorial(picknum) / 2
print(int(cnt))


Fixed submission:
# 後で１の位から処理するためにひっくり返しておく
S = list(map(int, reversed(input())))
#print("S = ", S)
"""
「2019の倍数か」は「mod 2019 が 0 になるか」と同意。
しかし、普通に切り出して、１つずつチェックすると計算工数がかかるので、累積和を使う。
各桁の値を （桁の数値a_k）× 10^k と表現し、mod 2019 の値を予め計算し、累積和を作っておく。
ここで、10^k の部分は、2019と互いに疎（公約数がない）なので、
含めて計算しても mod 2019 の周期を乱さない。（勿論オフセットは変わる）
「(i, j) が2019で割り切るか」は、「i～j の累積和が同じ値になる（2019周期）か」と同意。
累積和が同じ値になる個数を数えて、組み合わせを計算すればよい。
"""
# a　：　各桁の mod 2019 値（桁の数値 a_k × 10^k mod 2019）
a = [0] * len(S)
# sums ：　累積和
sums = [0] * (len(S) + 1)
# d : 10^k
d = 1
# cnt : 累積和 mod 2019 の出現度数表
cnt = [0] * 2019
cnt[0] = 1  # sums[0] = 0 の分
for i in range(len(S)):
    a[i] = (S[i] * d) % 2019
    sums[i + 1] = (sums[i] + a[i]) % 2019
    d = (d * 10) % 2019 
    cnt[sums[i + 1]] += 1
#print("sums = ", sums)
#print("cnt = ", cnt)
ans = 0
for i in cnt:
    ans += i * (i - 1) // 2
print(ans)

Buggy python submission (verdict: Time Limit Exceeded):
S = input()
cnt = 0
for ri in reversed(range(len(S))):
    for li in reversed(range(ri + 1)):
        s = S[li : ri + 1]
        #print("li = {}, ri = {}, s = {}".format(li, ri, s))
        if int(s) % 2019 == 0:
            #print("count ++ ")
            cnt += 1
print(cnt)


Fixed submission:
# 後で１の位から処理するためにひっくり返しておく
S = list(map(int, reversed(input())))
#print("S = ", S)
"""
「2019の倍数か」は「mod 2019 が 0 になるか」と同意。
しかし、普通に切り出して、１つずつチェックすると計算工数がかかるので、累積和を使う。
各桁の値を （桁の数値a_k）× 10^k と表現し、mod 2019 の値を予め計算し、累積和を作っておく。
ここで、10^k の部分は、2019と互いに疎（公約数がない）なので、
含めて計算しても mod 2019 の周期を乱さない。（勿論オフセットは変わる）
「(i, j) が2019で割り切るか」は、「i～j の累積和が同じ値になる（2019周期）か」と同意。
累積和が同じ値になる個数を数えて、組み合わせを計算すればよい。
"""
# a　：　各桁の mod 2019 値（桁の数値 a_k × 10^k mod 2019）
a = [0] * len(S)
# sums ：　累積和
sums = [0] * (len(S) + 1)
# d : 10^k
d = 1
# cnt : 累積和 mod 2019 の出現度数表
cnt = [0] * 2019
cnt[0] = 1  # sums[0] = 0 の分
for i in range(len(S)):
    a[i] = (S[i] * d) % 2019
    sums[i + 1] = (sums[i] + a[i]) % 2019
    d = (d * 10) % 2019 
    cnt[sums[i + 1]] += 1
#print("sums = ", sums)
#print("cnt = ", cnt)
ans = 0
for i in cnt:
    ans += i * (i - 1) // 2
print(ans)