├── Round 1A
├── append_sort.py
├── append_sort2.py
├── prime_time.py
└── hacked_exam.py
├── Round 1C
├── closest_pick.py
├── double_or_noting.py
├── roaring_years.py
├── double_or_noting2.py
└── double_or_noting3.py
├── Qualification Round
├── cheating_detection.test.py
├── reversort_engineering.py
├── reversort.py
├── reversort_engineering2.short.py
├── moons_and_umbrellas.py
├── cheating_detection4.py
├── cheating_detection.py
├── reversort_engineering2.py
├── cheating_detection3.py
├── median_sort.py
└── cheating_detection2.py
├── Round 2
├── matrygons.py
├── minimum_sort.py
├── hidden_pancakes2.py
├── hidden_pancakes.py
└── retiling.py
├── LICENSE
├── Round 1B
├── subtransmutation2.py
├── subtransmutation.py
├── broken_clock.py
└── digit_blocks.py
├── Virtual World Finals
├── slide_circuits3.py
├── slide_circuits.py
├── slide_circuits2.py
├── ropes.py
├── cutting_cake.py
├── divisible_divisions.py
├── divisible_divisions2.py
├── infinitree_concise.py3
├── infinitree_concise.py
└── infinitree.py
├── Round 3
├── build_a_pair3.py
├── build_a_pair.py
├── build_a_pair2.py
├── square_free.py
├── fence_design.py
├── binary_search_game.py
├── binary_search_game2.py
└── binary_search_game3.py
└── README.md
/Round 1A/append_sort.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 1A - Problem A. Append Sort
4 | # https://codingcompetitions.withgoogle.com/codejam/round/000000000043585d/00000000007549e5
5 | #
6 | # Time: O(N * log(MAX_X) * size(int)) = O(N * log(MAX_X))
7 | # Space: O(size(int)) = O(1)
8 | #
9 |
10 | def append_sort():
11 | N = input()
12 | X = map(int, raw_input().strip().split())
13 |
14 | result = 0
15 | for i in xrange(1, len(X)):
16 | cnt = 0
17 | while X[i] <= X[i-1]:
18 | X[i] *= 10
19 | cnt += 1
20 | if cnt > 1 and (X[i]//10) + (10**(cnt-1)-1) > X[i-1]:
21 | X[i] = X[i-1]+1
22 | cnt -= 1
23 | result += cnt
24 | return result
25 |
26 | for case in xrange(input()):
27 | print 'Case #%d: %s' % (case+1, append_sort())
28 |
--------------------------------------------------------------------------------
/Round 1C/closest_pick.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 1C - Problem A. Closest Pick
4 | # https://codingcompetitions.withgoogle.com/codejam/round/00000000004362d7/00000000007c0f00
5 | #
6 | # Time: O(NlogN)
7 | # Space: O(N)
8 | #
9 |
10 | def closest_pick():
11 | N, K = map(int, raw_input().strip().split())
12 | P = sorted(set(map(int, raw_input().strip().split())))
13 |
14 | result = prev_max = P[0]-1 # one or two in the first interval
15 | for i in xrange(1, len(P)):
16 | result = max(result, prev_max+(P[i]-P[i-1])//2, P[i]-P[i-1]-1) # one or two in this interval
17 | prev_max = max(prev_max, (P[i]-P[i-1])//2)
18 | return float(max(result, prev_max+(K-P[-1])))/K # one or two in the last interval
19 |
20 | for case in xrange(input()):
21 | print 'Case #%d: %s' % (case+1, closest_pick())
22 |
--------------------------------------------------------------------------------
/Qualification Round/cheating_detection.test.py:
--------------------------------------------------------------------------------
1 | # Usage: pypy cheating_detection.test.py >data.in 2>data.out
2 | # pypy cheating_detection.py < data.in > data.tmp && diff -y --suppress-common-lines data.tmp data.out | wc -l
3 |
4 | from random import uniform, randint, seed
5 | from math import exp
6 | from sys import stderr
7 |
8 | def f(x):
9 | return 1.0/(1.0+exp(-x))
10 |
11 | seed(0)
12 | T, P, S, Q = 1000, 86, 100, 10000
13 | print T
14 | print Q
15 | for case in xrange(T):
16 | s = [uniform(-3.0, 3.0) for _ in xrange(S)]
17 | q = [uniform(-3.0, 3.0) for _ in xrange(Q)]
18 | cheater = randint(0, S-1)
19 | print >>stderr, 'Case #%d: %s' % (case+1, cheater+1)
20 | for i in xrange(S):
21 | result = ['0']*Q
22 | for j in xrange(Q):
23 | if i == cheater and uniform(0.0, 1.0) <= 0.5:
24 | result[j] = '1'
25 | elif uniform(0.0, 1.0) <= f(s[i]-q[j]):
26 | result[j] = '1'
27 | print "".join(result)
28 |
--------------------------------------------------------------------------------
/Qualification Round/reversort_engineering.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Qualification Round - Problem C. Reversort Engineering
4 | # https://codingcompetitions.withgoogle.com/codejam/round/000000000043580a/00000000006d12d7
5 | #
6 | # Time: O(N^2)
7 | # Space: O(1)
8 | #
9 |
10 | def reverse(L, i, j):
11 | while i < j:
12 | L[i], L[j] = L[j], L[i]
13 | i += 1
14 | j -= 1
15 |
16 | def reversort_engineering():
17 | N, C = map(int, raw_input().strip().split())
18 |
19 | if not (N-1 <= C <= (N+2)*(N-1)//2):
20 | return "IMPOSSIBLE"
21 | result = range(1, N+1)
22 | for i in reversed(xrange(N-1)):
23 | l = min(C-i, N-i) # greedy
24 | C -= l
25 | # result[i:i+l] = result[i:i+l][::-1] # Space: O(N)
26 | reverse(result, i, i+l-1) # Space: O(1)
27 | return " ".join(map(str, result))
28 |
29 | for case in xrange(input()):
30 | print 'Case #%d: %s' % (case+1, reversort_engineering())
31 |
--------------------------------------------------------------------------------
/Round 2/matrygons.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 2 - Problem B. Matrygons
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000435915/00000000007dbf06
5 | #
6 | # Time: precompute: O(NlogN)
7 | # runtime: O(1)
8 | # Space: O(N)
9 | #
10 |
11 | def matrygons():
12 | N = input()
13 | return dp[N]
14 |
15 | MAX_N = 10**6
16 | pseudo = [0]*(MAX_N+1)
17 | dp = [0]*(MAX_N+1)
18 | for i in xrange(1, MAX_N//2+1): # time < sum(N/i for i in xrange(1, N)) = O(NlogN)
19 | for j in xrange(2*i, MAX_N+1, i): # min size of pseudo-polygon could be at least 2
20 | pseudo[j] = max(pseudo[j], pseudo[i-1]+1) # pseudo(j) = max(pseudo(i-1)+1 for each i s.t. j%i == 0 and j//i >= 2)
21 | if j//i >= 3: # min size of polygon should be at least 3
22 | dp[j] = max(dp[j], pseudo[i-1]+1) # dp(j) = max(pseudo(i-1)+1 for each i s.t. j%i == 0 and j//i >= 3)
23 | for case in xrange(input()):
24 | print 'Case #%d: %s' % (case+1, matrygons())
25 |
--------------------------------------------------------------------------------
/Qualification Round/reversort.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Qualification Round - Problem A. Reversort
4 | # https://codingcompetitions.withgoogle.com/codejam/round/000000000043580a/00000000006d0a5c
5 | #
6 | # Time: O(N^2)
7 | # Space: O(1)
8 | #
9 |
10 | def min_idx(L, i):
11 | m = i
12 | for j in xrange(i, len(L)):
13 | if L[j] < L[m]:
14 | m = j
15 | return m
16 |
17 | def reverse(L, i, j):
18 | while i < j:
19 | L[i], L[j] = L[j], L[i]
20 | i += 1
21 | j -= 1
22 |
23 | def reversort():
24 | N = input()
25 | L = map(int, raw_input().strip().split())
26 |
27 | result = 0
28 | for i in xrange(len(L)-1):
29 | # m = L.index(min(L[i:])) # Space: O(N)
30 | m = min_idx(L, i) # Space: O(1)
31 | # L[i:m+1] = L[i:m+1][::-1] # Space: O(N)
32 | reverse(L, i, m) # Space: O(1)
33 | result += m-i+1
34 | return result
35 |
36 | for case in xrange(input()):
37 | print 'Case #%d: %s' % (case+1, reversort())
38 |
--------------------------------------------------------------------------------
/Round 2/minimum_sort.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 2 - Problem A. Minimum Sort
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000435915/00000000007dc51c
5 | #
6 | # Time: O(ClogN) = C/N + C/(N-1) + ... + C/2 = 4.187 * 10^8 (given N = 100, C = 10^8)
7 | # Space: O(1)
8 | #
9 | # Usage: python interactive_runner.py python3 testing_tool.py -- python minimum_sort.py
10 | #
11 |
12 | from sys import stdout
13 |
14 | def query(i, j):
15 | print "M %s %s" % (i, j)
16 | stdout.flush()
17 | return input()
18 |
19 | def swap(i, j):
20 | print "S %s %s" % (i, j)
21 | stdout.flush()
22 | return input()
23 |
24 | def done():
25 | print "D"
26 | stdout.flush()
27 | return input()
28 |
29 | def minimum_sort():
30 | for i in xrange(1, N):
31 | idx = query(i, N)
32 | if idx != i:
33 | swap(i, idx)
34 | if done() != 1:
35 | exit()
36 |
37 | T, N = map(int, raw_input().strip().split())
38 | for case in xrange(T):
39 | minimum_sort()
40 |
--------------------------------------------------------------------------------
/Qualification Round/reversort_engineering2.short.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Qualification Round - Problem C. Reversort Engineering
4 | # https://codingcompetitions.withgoogle.com/codejam/round/000000000043580a/00000000006d12d7
5 | #
6 | # Time: O(N)
7 | # Space: O(N), shorter, but with extra space O(N)
8 | #
9 |
10 | def reversort_engineering():
11 | N, C = map(int, raw_input().strip().split())
12 |
13 | if not (N-1 <= C <= (N+2)*(N-1)//2):
14 | return "IMPOSSIBLE"
15 | result = [0]*N
16 | for i in xrange(N):
17 | l = min(C-(N-1-i)+1, N-i) # greedy
18 | C -= l
19 | fill = (list(reversed(range(i+1, i+1+l))) + range(i+1+l, N+1)) if l != N-i else [i+1]
20 | if i%2 == 0:
21 | result[N-1-i//2+1-len(fill):N-1-i//2+1] = fill
22 | else:
23 | result[i//2:i//2+len(fill)] = fill[::-1]
24 | if l != N-i:
25 | break
26 | return " ".join(map(str, result))
27 |
28 | for case in xrange(input()):
29 | print 'Case #%d: %s' % (case+1, reversort_engineering())
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 kamyu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Qualification Round/moons_and_umbrellas.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Qualification Round - Problem B. Moons and Umbrellas
4 | # https://codingcompetitions.withgoogle.com/codejam/round/000000000043580a/00000000006d1145
5 | #
6 | # Time: O(N)
7 | # Space: O(1)
8 | #
9 |
10 | def moons_and_umbrellas():
11 | X, Y, S = raw_input().strip().split()
12 | X, Y = int(X), int(Y)
13 | dp = {}
14 | prev = None
15 | for c in S:
16 | new_dp = {}
17 | for i, j, cost in [('C', 'J', Y), ('J', 'C', X)]:
18 | if c == j:
19 | new_dp[i] = INF
20 | elif prev is None:
21 | new_dp[i] = 0
22 | elif prev == i:
23 | new_dp[i] = dp[i]
24 | elif prev == j:
25 | new_dp[i] = dp[j]+cost
26 | elif prev == '?':
27 | new_dp[i] = min(dp[i], dp[j]+cost)
28 | dp = new_dp
29 | prev = c
30 | return min(dp.itervalues())
31 |
32 | INF = float("inf")
33 | for case in xrange(input()):
34 | print 'Case #%d: %s' % (case+1, moons_and_umbrellas())
35 |
--------------------------------------------------------------------------------
/Round 1A/append_sort2.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 1A - Problem A. Append Sort
4 | # https://codingcompetitions.withgoogle.com/codejam/round/000000000043585d/00000000007549e5
5 | #
6 | # Time: O(N * log(MAX_X))
7 | # Space: O(log(MAX_X))
8 | #
9 |
10 | def append_sort():
11 | N = input()
12 | X = map(list, raw_input().strip().split())
13 |
14 | result = 0
15 | curr = list(X[0])
16 | for i in xrange(1, len(X)):
17 | prev, curr = curr, list(X[i])
18 | if len(curr) > len(prev):
19 | continue
20 | result += (len(prev)-len(curr))
21 | if curr[:len(curr)] < prev[:len(curr)]:
22 | curr.extend(['0']*((len(prev)-len(curr))+1))
23 | result += 1
24 | elif curr[:len(curr)] > prev[:len(curr)]:
25 | curr.extend(['0']*(len(prev)-len(curr)))
26 | else:
27 | if len(prev)-len(curr) != 0 and int("".join(prev[len(curr):]))+1 < 10**(len(prev)-len(curr)):
28 | curr.extend(list(str(int("".join(prev[len(curr):]))+1).zfill((len(prev)-len(curr)))))
29 | else:
30 | curr.extend(['0']*((len(prev)-len(curr))+1))
31 | result += 1
32 | return result
33 |
34 | for case in xrange(input()):
35 | print 'Case #%d: %s' % (case+1, append_sort())
36 |
--------------------------------------------------------------------------------
/Qualification Round/cheating_detection4.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Qualification Round - Problem E. Cheating Detection
4 | # https://codingcompetitions.withgoogle.com/codejam/round/000000000043580a/00000000006d1155
5 | #
6 | # Time: O(S * Q)
7 | # Space: O(S + Q)
8 | #
9 | # Correlation coefficient method
10 | # Accuracy: 895/1000 = 89.5%
11 | #
12 |
13 | def normalized(a):
14 | avg = float(sum(a))/len(a)
15 | for i in xrange(len(a)):
16 | a[i] -= avg
17 |
18 | def covariance(a, b):
19 | return sum(a[i]*b[i] for i in xrange(len(a)))
20 |
21 | def correlation(a, b):
22 | return covariance(a, b) / covariance(a, a)**0.5 / covariance(b, b)**0.5
23 |
24 | def cheating_detection():
25 | scores = []
26 | q_count = [0]*Q
27 | for i in xrange(S):
28 | scores.append(map(int, list(raw_input().strip())))
29 | for j, c in enumerate(scores[i]):
30 | q_count[j] += c
31 | normalized(q_count)
32 | result, min_corr = 0, 1.0
33 | for i, score in enumerate(scores):
34 | normalized(score)
35 | corr = correlation(score, q_count)
36 | if corr < min_corr:
37 | min_corr = corr
38 | result = i
39 | return result+1
40 |
41 | S, Q, T, P = 100, 10000, input(), input()
42 | for case in xrange(T):
43 | print 'Case #%d: %s' % (case+1, cheating_detection())
44 |
--------------------------------------------------------------------------------
/Round 1B/subtransmutation2.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 1B - Problem B. Subtransmutation
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000435baf/00000000007ae4aa
5 | #
6 | # Time: O(MAX_M^2), MAX_M is the max possible m in the given limit
7 | # Space: O(MAX_M)
8 | #
9 |
10 | from fractions import gcd
11 |
12 | def get_U(U, i):
13 | return U[i] if i < len(U) else 0
14 |
15 | def check(A, B, U, x):
16 | count = [0]*x
17 | count[-1] = 1
18 | for i in reversed(xrange(len(count))):
19 | if count[i] < get_U(U, i):
20 | return False
21 | extra = count[i]-get_U(U, i)
22 | if A <= i:
23 | count[i-A] += extra
24 | if B <= i:
25 | count[i-B] += extra
26 | return True
27 |
28 | def subtransmutation():
29 | N, A, B = map(int, raw_input().strip().split())
30 | U = map(int, raw_input().strip().split())
31 | g = gcd(A, B)
32 | k = None
33 | for i, c in enumerate(U, 1):
34 | if not c:
35 | continue
36 | if k is None:
37 | k = i%g
38 | continue
39 | if i%g != k:
40 | return "IMPOSSIBLE"
41 | result = (N+1)+(k-(N+1))%g
42 | while not check(A, B, U, result):
43 | result += g
44 | return result
45 |
46 | for case in xrange(input()):
47 | print 'Case #%d: %s' % (case+1, subtransmutation())
48 |
--------------------------------------------------------------------------------
/Qualification Round/cheating_detection.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Qualification Round - Problem E. Cheating Detection
4 | # https://codingcompetitions.withgoogle.com/codejam/round/000000000043580a/00000000006d1155
5 | #
6 | # Time: O(S * Q + QlogQ)
7 | # Space: O(S + Q)
8 | #
9 | # Inversion count method
10 | # Accuracy: 996/1000 = 99.6%
11 | #
12 |
13 | def cheating_detection():
14 | scores = []
15 | q_count = [0]*Q
16 | for i in xrange(S):
17 | scores.append(map(int, list(raw_input().strip())))
18 | for j, c in enumerate(scores[i]):
19 | q_count[j] += c
20 | questions = sorted(range(Q), key=lambda x:q_count[x])
21 | result, max_score = 0, 0.0
22 | for i in xrange(S):
23 | cnt = [0]*2
24 | inv = 0
25 | for j in questions:
26 | if not scores[i][j]:
27 | cnt[0] += 1
28 | inv += cnt[1]
29 | else:
30 | cnt[1] += 1
31 | score = float(inv)/(1+cnt[0])/(1+cnt[1]) # normalize inversions by weakness and strength, both ends are divided less and the middle are divided more
32 | if score > max_score: # the higher score is, the more uniform distribution of corrects is
33 | max_score = score
34 | result = i
35 | return result+1
36 |
37 | S, Q, T, P = 100, 10000, input(), input()
38 | for case in xrange(T):
39 | print 'Case #%d: %s' % (case+1, cheating_detection())
40 |
--------------------------------------------------------------------------------
/Qualification Round/reversort_engineering2.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Qualification Round - Problem C. Reversort Engineering
4 | # https://codingcompetitions.withgoogle.com/codejam/round/000000000043580a/00000000006d12d7
5 | #
6 | # Time: O(N)
7 | # Space: O(1)
8 | #
9 |
10 | def reverse(L, i, j):
11 | while i < j:
12 | L[i], L[j] = L[j], L[i]
13 | i += 1
14 | j -= 1
15 |
16 | def reversort_engineering():
17 | N, C = map(int, raw_input().strip().split())
18 |
19 | if not (N-1 <= C <= (N+2)*(N-1)//2):
20 | return "IMPOSSIBLE"
21 | result = [0]*N
22 | for i in xrange(N):
23 | l = min(C-(N-1-i)+1, N-i) # greedy
24 | C -= l
25 | if l != N-i:
26 | break
27 | if i%2 == 0:
28 | result[N-1-i//2] = i+1
29 | else:
30 | result[i//2] = i+1
31 | if i%2 == 0:
32 | k = i+1
33 | for j in xrange((N-1-i//2+1)-(N-i), N-1-i//2+1):
34 | result[j] = k
35 | k += 1
36 | reverse(result, (N-1-i//2+1)-(N-i), ((N-1-i//2+1)-(N-i))+l-1) # Space: O(1)
37 | else:
38 | k = i+1
39 | for j in reversed(xrange(i//2, i//2+(N-i))):
40 | result[j] = k
41 | k += 1
42 | reverse(result, (i//2+(N-i)-1)-l+1, i//2+(N-i)-1) # Space: O(1)
43 | return " ".join(map(str, result))
44 |
45 | for case in xrange(input()):
46 | print 'Case #%d: %s' % (case+1, reversort_engineering())
47 |
--------------------------------------------------------------------------------
/Round 1B/subtransmutation.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 1B - Problem B. Subtransmutation
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000435baf/00000000007ae4aa
5 | #
6 | # Time: O(MAX_M^2), MAX_M is the max possible m in the given limit
7 | # Space: O(MAX_M)
8 | #
9 |
10 | def get_U(U, i):
11 | return U[i] if i < len(U) else 0
12 |
13 | def check(A, B, U, x):
14 | count = [0]*x
15 | count[-1] = 1
16 | for i in reversed(xrange(len(count))):
17 | if count[i] < get_U(U, i):
18 | return False
19 | extra = count[i]-get_U(U, i)
20 | if A <= i:
21 | count[i-A] += extra
22 | if B <= i:
23 | count[i-B] += extra
24 | return True
25 |
26 | def subtransmutation():
27 | N, A, B = map(int, raw_input().strip().split())
28 | U = map(int, raw_input().strip().split())
29 | result = N+1
30 | while result <= MAX_M:
31 | if check(A, B, U, result):
32 | return result
33 | result += 1
34 | return "IMPOSSIBLE"
35 |
36 | '''
37 | GUESS = 1000
38 | MAX_N = MAX_U = 20
39 | MAX_A = MAX_B = 20
40 | U = [MAX_U]*MAX_N
41 | MAX_M = 0
42 | for A in xrange(1, MAX_A+1):
43 | for B in xrange(A+1, MAX_B+1):
44 | result = MAX_N+1
45 | while result <= GUESS:
46 | if check(A, B, U, result):
47 | MAX_M = max(MAX_M, result)
48 | break
49 | result += 1
50 | assert(MAX_M == 402)
51 | '''
52 | MAX_M = 402
53 | for case in xrange(input()):
54 | print 'Case #%d: %s' % (case+1, subtransmutation())
55 |
--------------------------------------------------------------------------------
/Qualification Round/cheating_detection3.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Qualification Round - Problem E. Cheating Detection
4 | # https://codingcompetitions.withgoogle.com/codejam/round/000000000043580a/00000000006d1155
5 | #
6 | # Time: O(S * Q + SlogS + QlogQ)
7 | # Space: O(S + Q)
8 | #
9 | # Difference with expected distribution method
10 | # Accuracy: 1000/1000 = 100.0%
11 | #
12 |
13 | from math import exp
14 |
15 | def f(x):
16 | return 1.0/(1.0+exp(-x))
17 |
18 | def cheating_detection():
19 | scores = []
20 | p_count = [0]*S
21 | q_count = [0]*Q
22 | for i in xrange(S):
23 | scores.append(map(int, list(raw_input().strip())))
24 | for j, c in enumerate(scores[i]):
25 | p_count[i] += c
26 | q_count[j] += c
27 | players = sorted(range(S), key=lambda x:p_count[x])
28 | questions = sorted(range(Q), key=lambda x:q_count[x])
29 | si = MIN
30 | result, max_score, si = 0, 0.0, MIN
31 | for i in players:
32 | score, qj = 0.0, MAX
33 | for j in questions:
34 | score += (scores[i][j]-f(si-qj))**2
35 | qj -= Q_D
36 | score /= (1+p_count[i])*(1+(Q-p_count[i])) # normalize score by weakness and strength, both ends are divided less and the middle are divided more
37 | if score > max_score:
38 | max_score = score
39 | result = i
40 | si += S_D
41 | return result+1
42 |
43 | MIN, MAX = -3.0, 3.0
44 | S, Q, = 100, 10000
45 | S_D, Q_D = (MAX-MIN)/S, (MAX-MIN)/Q
46 | T, P = input(), input()
47 | for case in xrange(T):
48 | print 'Case #%d: %s' % (case+1, cheating_detection())
49 |
--------------------------------------------------------------------------------
/Qualification Round/median_sort.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Qualification Round - Problem D. Median Sort
4 | # https://codingcompetitions.withgoogle.com/codejam/round/000000000043580a/00000000006d1284
5 | #
6 | # Time: O(N^2)
7 | # Space: O(1)
8 | # Usage: python interactive_runner.py python3 testing_tool.py 2 -- python median_sort.py
9 | #
10 |
11 | from sys import stdout
12 |
13 | def query(i, j, k):
14 | print i, j, k
15 | stdout.flush()
16 | return input()
17 |
18 | def check(result):
19 | print " ".join(map(str, result))
20 | stdout.flush()
21 | ok = raw_input().strip()
22 | if ok != "1": # error
23 | exit()
24 |
25 | def median_sort():
26 | result = [1, 2]
27 | for i in xrange(3, N+1): # Time: O(N)
28 | left, right = 0, len(result)-1
29 | while right-left >= 1: # Time: O(logN)
30 | m1 = left + (right-left)//3
31 | m2 = right - (right-left)//3
32 | x = query(result[m1], result[m2], i)
33 | if x == result[m1]:
34 | right = m1-1
35 | if left == right: # padded for the last query
36 | right += 1
37 | elif x == result[m2]:
38 | left = m2+1
39 | if left == right: # padded for the last query
40 | left -= 1
41 | else:
42 | left, right = m1+1, m2-1
43 | if left == right: # padded for the last query
44 | left -= 1
45 | result.insert(left, i) # Time: O(N)
46 | check(result)
47 |
48 | T, N, Q = map(int, raw_input().strip().split())
49 | for case in xrange(T):
50 | median_sort()
51 |
--------------------------------------------------------------------------------
/Round 1C/double_or_noting.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 1C - Problem C. Double or NOTing
4 | # https://codingcompetitions.withgoogle.com/codejam/round/00000000004362d7/00000000007c1139
5 | #
6 | # Time: O(K * (|E| + |S|)), K is the number of bit groups of S
7 | # Space: O(|E| + |S|)
8 | #
9 | # shorter but slower solution
10 | #
11 |
12 | # from re import match
13 |
14 | def flip(s):
15 | return "".join(["01"[c == '0'] for c in s]).lstrip('0') or "0"
16 |
17 | def not_count(s):
18 | s += '0' # if s ends with '1', it requires one more "not" operation, which could be easily counted by appending a '0'
19 | return sum(int(s[i] != s[i+1]) for i in reversed(xrange(len(s)-1)))
20 |
21 | def find_prefix_and_count(S, E):
22 | result = float("inf")
23 | X = 0
24 | while S != "0":
25 | if S == E[:len(S)] and X >= not_count(E[len(S):]):
26 | return X+(len(E)-len(S)), None
27 | S = flip(S)
28 | X += 1
29 | return result, X
30 |
31 | def double_or_noting():
32 | S, E = raw_input().strip().split()
33 |
34 | result, X = find_prefix_and_count(S, E)
35 | if result != float("inf"):
36 | return result
37 | if X >= not_count(E):
38 | return X+len(E)-(E[0] == '0')
39 | cnt = not_count(E[1:])
40 | if cnt == 0:
41 | # assert(match("^10*$", E))
42 | return X+1+(len(E)-1) # S =X=> "0" =1=> "1" =(len(E)-1)=> "10*"
43 | if cnt == 1:
44 | # assert(match("^11+0*$", E))
45 | return X+1+len(E)+1 # S =X=> "0" =1=> "1" =k=> "100+" =1=> "11+" =(len(E)-k)=> "11+0*", where 2 <= k <= len(E)
46 | return "IMPOSSIBLE"
47 |
48 | for case in xrange(input()):
49 | print 'Case #%d: %s' % (case+1, double_or_noting())
50 |
--------------------------------------------------------------------------------
/Round 1B/broken_clock.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 1B - Problem A. Broken Clock
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000435baf/00000000007ae694
5 | #
6 | # Time: O(1)
7 | # Space: O(1)
8 | #
9 |
10 | from itertools import permutations
11 |
12 | def linear_congruence(a, m, b): # Time: O(logN), the same as gcd, Space: O(logN)
13 | # gcd(a, m) = g and g|b, find x, s.t. ax % m = b % m
14 | # => (a%m)x = my+(b%m)
15 | # => gcd(m, a%m) = g and g|-(b%m), find y, s.t. my % (a%m) = -(b%m) % (a%m)
16 | # => y = linear_congruence(m, a%m, -(b%m))
17 | # => x = (my+(b%m))/(a%m)
18 | ambs = []
19 | while m:
20 | a, m, b = m, a%m, -(b%m)
21 | if m:
22 | ambs.append((m, a, -b))
23 | x = a # a is gcd
24 | while ambs:
25 | a, m, b = ambs.pop()
26 | x = (m*x+b)//a
27 | return x
28 |
29 | def format_ticks(t):
30 | s, n = divmod(t, TICKS_PER_SECOND)
31 | m, s = divmod(s, 60)
32 | h, m = divmod(m, 60)
33 | return "%s %s %s %s" % (h, m, s, n)
34 |
35 | def solution():
36 | A, B, C = map(int, raw_input().strip().split())
37 | # t%TOTAL = (h+x)%TOTAL
38 | # 12*t%TOTAL = (m+x)%TOTAL
39 | # 720*t%TOTAL = (s+x)%TOTAL
40 | # => find t s.t.
41 | # 11*t%TOTAL = (m-h)%TOTAL
42 | # 708*t%TOTAL = (s-m)%TOTAL
43 | for h, m, s in set(permutations([A, B, C])):
44 | t = INV_11*(m-h)%TOTAL
45 | if 708*t%TOTAL == (s-m)%TOTAL:
46 | break
47 | return format_ticks(t)
48 |
49 | TICKS_PER_SECOND = 10**9
50 | TOTAL = 12*60*60*TICKS_PER_SECOND
51 | INV_11 = linear_congruence(11, TOTAL, 1)%TOTAL # Time: O(log(min(11, TOTAL))) = O(log11) = O(1)
52 | for case in xrange(input()):
53 | print 'Case #%d: %s' % (case+1, solution())
54 |
--------------------------------------------------------------------------------
/Round 1C/roaring_years.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 1C - Problem B. Roaring Years
4 | # https://codingcompetitions.withgoogle.com/codejam/round/00000000004362d7/00000000007c0f01
5 | #
6 | # Time: O(D^2 * logD), D is the digit count of Y, since sum((D/n)*D for n in [2, D+1]) = D^2 * O(logD)
7 | # Space: O(D)
8 | #
9 |
10 | def ceil(x, n):
11 | return (x-1)//n+1
12 |
13 | def floor(x, n):
14 | return x//n
15 |
16 | def f(x, n):
17 | return int("".join(str(i) for i in xrange(x, x+n)))
18 |
19 | def binary_search(left, right, check):
20 | while left <= right:
21 | mid = left + (right-left)//2
22 | if check(mid):
23 | right = mid-1
24 | else:
25 | left = mid+1
26 | return left
27 |
28 | def min_fn(Y, n):
29 | y = int(Y)
30 | # find any x, s.t. f(x, n) > y
31 | # => X = str(x), f(x, n) > X*n >= 10**len(Y) > y
32 | # => len(X)*n >= len(Y)+1
33 | # => len(X) = ceil(len(Y)+1, n)
34 | # => x is in the range [10**(ceil(len(Y)+1, n)-1), 10**ceil(len(Y)+1, n)-1]
35 | # => let right be 10**(ceil(len(Y)+1, n)-1)
36 | right = 10**(ceil(len(Y)+1, n)-1)
37 | assert(f(right, n) > y)
38 | # find any x, s.t. f(x, n) <= y
39 | # => X = str(x), f(x, n) < X*n <= 10**(len(Y)-1) <= y
40 | # => len(X)*n <= len(Y)
41 | # => len(X) = floor(len(Y), n)
42 | # => x is in the range [1, 10**(floor(len(Y), n)-1)-n+1]
43 | # => let left be max(1, 10**(floor(len(Y), n)-1)-n+1)
44 | left = max(1, 10**(floor(len(Y), n)-1)-n+1)
45 | x = binary_search(left, right, lambda x: f(x, n) > y) # find the smallest x, s.t. f(x, n) > y
46 | return f(x, n)
47 |
48 | def roaring_years():
49 | Y = raw_input().strip()
50 | return min(min_fn(Y, n) for n in xrange(2, (len(Y)+1)+1))
51 |
52 | for case in xrange(input()):
53 | print 'Case #%d: %s' % (case+1, roaring_years())
54 |
--------------------------------------------------------------------------------
/Qualification Round/cheating_detection2.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Qualification Round - Problem E. Cheating Detection
4 | # https://codingcompetitions.withgoogle.com/codejam/round/000000000043580a/00000000006d1155
5 | #
6 | # Time: O(S * Q + SlogS + QlogQ)
7 | # Space: O(S + Q)
8 | #
9 | # Difference with neighbors in easiest and hardest 5% questions method
10 | # Accuracy: 982/1000 = 98.2%
11 | #
12 |
13 | def diff(player1, player2, extreme_questions):
14 | return abs(sum(player1[j] for j in extreme_questions) - sum(player2[j] for j in extreme_questions))
15 |
16 | def neighbor_diffs(scores, players, extreme_questions, i):
17 | diffs = cnt = 0
18 | if i-1 >= 0:
19 | diffs += diff(scores[players[i-1]], scores[players[i]], extreme_questions)
20 | cnt += 1
21 | if i+1 < S:
22 | diffs += diff(scores[players[i]], scores[players[i+1]], extreme_questions)
23 | cnt += 1
24 | return float(diffs)/cnt
25 |
26 | def cheating_detection():
27 | scores = []
28 | p_count = [0]*S
29 | q_count = [0]*Q
30 | for i in xrange(S):
31 | scores.append(map(int, list(raw_input().strip())))
32 | for j, c in enumerate(scores[i]):
33 | p_count[i] += c
34 | q_count[j] += c
35 | players = sorted(range(S), key=lambda x:p_count[x])
36 | questions = sorted(range(Q), key=lambda x:q_count[x])
37 | extreme_questions = [questions[j] for j in xrange(int(Q*EXTREME_RATIO))] + [questions[j] for j in xrange(Q-int(Q*EXTREME_RATIO), Q)]
38 | result = 0
39 | for i in xrange(S):
40 | if neighbor_diffs(scores, players, extreme_questions, i) > neighbor_diffs(scores, players, extreme_questions, result):
41 | result = i
42 | return players[result]+1
43 |
44 | EXTREME_RATIO = 0.05
45 | S, Q, T, P = 100, 10000, input(), input()
46 | for case in xrange(T):
47 | print 'Case #%d: %s' % (case+1, cheating_detection())
48 |
--------------------------------------------------------------------------------
/Round 2/hidden_pancakes2.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 2 - Problem C. Hidden Pancakes
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000435915/00000000007dc20c
5 | #
6 | # Time: O(N)
7 | # Space: O(N)
8 | #
9 |
10 | def factorial(n):
11 | while len(fact) <= n: # lazy initialization
12 | fact.append(fact[-1]*len(fact) % MOD)
13 | return fact[n]
14 |
15 | def inverse(n):
16 | while len(inv) <= n: # lazy initialization
17 | inv.append(inv[MOD%len(inv)]*(MOD-MOD//len(inv)) % MOD) # https://cp-algorithms.com/algebra/module-inverse.html
18 | return inv[n]
19 |
20 | def hidden_pancakes():
21 | N = input()
22 | V = map(int, raw_input().strip().split())
23 | V.append(1) # add a virtual value to count the permutation of remaining subtrees
24 | result = factorial(N) # max number of permutations
25 | stk = [] # keep the size of each subtree satisfying v
26 | for v in V:
27 | if not (v <= len(stk)+1): # v minus the number of subtrees should be less than or equal to 1
28 | return 0
29 | cnt = 0
30 | while v < len(stk)+1: # pop subtree size and form a new tree until the number of subtrees on stack is v
31 | # a tree structure is constructed by v, count the valid permutations:
32 | # the largest pancake size of each subtee on stack keeps monotonically decreasing.
33 | # since the total size of the current merged tree is cnt, we can only choose the largest pancake as root from cnt pancakes.
34 | # thus we could inversely get the true number of valid permutations from max number of permutations.
35 | # so the number of valid permutations is as follows:
36 | cnt += stk.pop()
37 | result = (result * inverse(cnt)) % MOD
38 | stk.append(cnt+1) # len(stk) == v
39 | return result
40 |
41 | MOD = 10**9+7
42 | fact = [1, 1]
43 | inv = [0, 1]
44 | for case in xrange(input()):
45 | print 'Case #%d: %s' % (case+1, hidden_pancakes())
46 |
--------------------------------------------------------------------------------
/Round 2/hidden_pancakes.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 2 - Problem C. Hidden Pancakes
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000435915/00000000007dc20c
5 | #
6 | # Time: O(N)
7 | # Space: O(N)
8 | #
9 |
10 | def nCr(n, k):
11 | while len(inv) <= n: # lazy initialization
12 | fact.append(fact[-1]*len(inv) % MOD)
13 | inv.append(inv[MOD%len(inv)]*(MOD-MOD//len(inv)) % MOD) # https://cp-algorithms.com/algebra/module-inverse.html
14 | inv_fact.append(inv_fact[-1]*inv[-1] % MOD)
15 | return (fact[n]*inv_fact[n-k] % MOD) * inv_fact[k] % MOD
16 |
17 | def hidden_pancakes():
18 | N = input()
19 | V = map(int, raw_input().strip().split())
20 | V.append(1) # add a virtual value to count the permutation of remaining subtrees
21 | result = 1
22 | stk = [] # keep the size of each subtree satisfying v
23 | for v in V:
24 | if not (v <= len(stk)+1): # v minus the number of subtrees should be less than or equal to 1
25 | return 0
26 | cnt = 0
27 | while v < len(stk)+1: # pop subtree size and form a new tree until the number of subtrees on stack is v
28 | # a tree structure is constructed by v, count the valid permutations:
29 | # the largest pancake size of each subtee on stack keeps monotonically decreasing.
30 | # to merge the subtree on the top of stack, use its largest pancake as root of a new tree,
31 | # the rest part of the subtree is as a left subtree with size stk[-1]-1,
32 | # and the previously merged subtree is as a right subtree with size cnt.
33 | # so the number of valid permutations is as follows:
34 | result = result * nCr(cnt+(stk[-1]-1), (stk[-1]-1)) % MOD
35 | cnt += stk.pop()
36 | stk.append(cnt+1) # len(stk) == v
37 | return result
38 |
39 | MOD = 10**9+7
40 | fact = [1, 1]
41 | inv = [0, 1]
42 | inv_fact = [1, 1]
43 | for case in xrange(input()):
44 | print 'Case #%d: %s' % (case+1, hidden_pancakes())
--------------------------------------------------------------------------------
/Virtual World Finals/slide_circuits3.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Virtual World Finals - Problem B. Slide Circuits
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000436329/000000000084f7b2
5 | #
6 | # Time: O(B + N + SlogS)
7 | # Space: O(B + SlogS)
8 | #
9 | # polynomial hashes solution (slower)
10 | #
11 |
12 | def poly_hash_id_gen(B, basis):
13 | for _ in xrange(B):
14 | yield basis[0]
15 | basis[0] = (basis[0]*p)%MOD
16 |
17 | def get_sum(prefix, L, R, M):
18 | return sub(prefix[M][R//M], prefix[M][(L-1)//M])
19 |
20 | def slide_circuits():
21 | B, S, N = map(int, raw_input().strip().split())
22 | basis = [1]
23 | in_ids = [i for i in poly_hash_id_gen(B, basis)]
24 | out_ids = [i for i in poly_hash_id_gen(B, basis)]
25 | total_hash = add(reduce(add, in_ids), reduce(add, out_ids))
26 | slide_hashes = [0]*S
27 | lookup = {}
28 | for i in xrange(S):
29 | X, Y = map(int, raw_input().strip().split())
30 | slide_hashes[i] = add(out_ids[X-1], in_ids[Y-1])
31 | lookup[sub(total_hash, slide_hashes[i])] = i # 0-indexed
32 | prefix = [[0] for _ in xrange(S+1)]
33 | for m in xrange(1, S+1): # sum of harmonic series: O(S/1 + S/2 + ... + S/S) = O(S * (1 + 1/2 + ... + 1/S)) = O(SlogS)
34 | for i in xrange(m, S+1, m):
35 | prefix[m].append(add(prefix[m][-1], slide_hashes[i-1]))
36 | result = [0]*N
37 | curr_hash = 0
38 | for i in xrange(N):
39 | A, L, R, M = raw_input().strip().split()
40 | L, R, M = int(L), int(R), int(M)
41 | curr_hash = HASH_OP[A](curr_hash, get_sum(prefix, L, R, M))
42 | if curr_hash in lookup:
43 | result[i] = lookup[curr_hash]+1
44 | return " ".join(map(lambda x: str(x) if x else "X", result))
45 |
46 | MOD, p = 2**64-59, 113 # MOD is max 64-bit prime
47 | add = lambda a, b: (a+b)%MOD
48 | sub = lambda a, b: (a-b)%MOD
49 | HASH_OP = {'E':add, 'D':sub}
50 | for case in xrange(input()):
51 | print 'Case #%d: %s' % (case+1, slide_circuits())
52 |
--------------------------------------------------------------------------------
/Virtual World Finals/slide_circuits.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Virtual World Finals - Problem B. Slide Circuits
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000436329/000000000084f7b2
5 | #
6 | # Time: O(B + N + SlogS)
7 | # Space: O(B + SlogS)
8 | #
9 | # sum of random values solution
10 | #
11 |
12 | from random import seed, randint
13 | from operator import add, sub
14 |
15 | def random_id_gen(B, ids_set):
16 | MAX_RAND_ID = MAX_UINT64//(B*2)
17 | for _ in xrange(B):
18 | x = randint(1, MAX_RAND_ID)
19 | while True:
20 | x = randint(1, MAX_RAND_ID)
21 | if x not in ids_set:
22 | break
23 | ids_set.add(x)
24 | yield x
25 |
26 | def get_sum(prefix, L, R, M):
27 | return sub(prefix[M][R//M], prefix[M][(L-1)//M])
28 |
29 | def slide_circuits():
30 | B, S, N = map(int, raw_input().strip().split())
31 | ids_set = set()
32 | in_ids = [i for i in random_id_gen(B, ids_set)]
33 | out_ids = [i for i in random_id_gen(B, ids_set)]
34 | total_hash = add(reduce(add, in_ids), reduce(add, out_ids))
35 | slide_hashes = [0]*S
36 | lookup = {}
37 | for i in xrange(S):
38 | X, Y = map(int, raw_input().strip().split())
39 | slide_hashes[i] = add(out_ids[X-1], in_ids[Y-1])
40 | lookup[sub(total_hash, slide_hashes[i])] = i # 0-indexed
41 | prefix = [[0] for _ in xrange(S+1)]
42 | for m in xrange(1, S+1): # sum of harmonic series: O(S/1 + S/2 + ... + S/S) = O(S * (1 + 1/2 + ... + 1/S)) = O(SlogS)
43 | for i in xrange(m, S+1, m):
44 | prefix[m].append(add(prefix[m][-1], slide_hashes[i-1]))
45 | result = [0]*N
46 | curr_hash = 0
47 | for i in xrange(N):
48 | A, L, R, M = raw_input().strip().split()
49 | L, R, M = int(L), int(R), int(M)
50 | curr_hash = HASH_OP[A](curr_hash, get_sum(prefix, L, R, M))
51 | if curr_hash in lookup:
52 | result[i] = lookup[curr_hash]+1
53 | return " ".join(map(lambda x: str(x) if x else "X", result))
54 |
55 | seed(0)
56 | MAX_UINT64 = 2**64-1
57 | HASH_OP = {'E':add, 'D':sub}
58 | for case in xrange(input()):
59 | print 'Case #%d: %s' % (case+1, slide_circuits())
60 |
--------------------------------------------------------------------------------
/Virtual World Finals/slide_circuits2.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Virtual World Finals - Problem B. Slide Circuits
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000436329/000000000084f7b2
5 | #
6 | # Time: O(B + N + SlogS)
7 | # Space: O(B + SlogS)
8 | #
9 | # xor of random values solution (faster)
10 | #
11 |
12 | from random import seed, randint
13 | from operator import xor
14 |
15 | def random_id_gen(B, ids_set):
16 | MAX_RAND_ID = MAX_UINT64//(B*2)
17 | for _ in xrange(B):
18 | x = randint(1, MAX_RAND_ID)
19 | while True:
20 | x = randint(1, MAX_RAND_ID)
21 | if x not in ids_set:
22 | break
23 | ids_set.add(x)
24 | yield x
25 |
26 | def get_sum(prefix, L, R, M):
27 | return sub(prefix[M][R//M], prefix[M][(L-1)//M])
28 |
29 | def slide_circuits():
30 | B, S, N = map(int, raw_input().strip().split())
31 | ids_set = set()
32 | in_ids = [i for i in random_id_gen(B, ids_set)]
33 | out_ids = [i for i in random_id_gen(B, ids_set)]
34 | total_hash = add(reduce(add, in_ids), reduce(add, out_ids))
35 | slide_hashes = [0]*S
36 | lookup = {}
37 | for i in xrange(S):
38 | X, Y = map(int, raw_input().strip().split())
39 | slide_hashes[i] = add(out_ids[X-1], in_ids[Y-1])
40 | lookup[sub(total_hash, slide_hashes[i])] = i # 0-indexed
41 | prefix = [[0] for _ in xrange(S+1)]
42 | for m in xrange(1, S+1): # sum of harmonic series: O(S/1 + S/2 + ... + S/S) = O(S * (1 + 1/2 + ... + 1/S)) = O(SlogS)
43 | for i in xrange(m, S+1, m):
44 | prefix[m].append(add(prefix[m][-1], slide_hashes[i-1]))
45 | result = [0]*N
46 | total = curr_hash = 0
47 | for i in xrange(N):
48 | A, L, R, M = raw_input().strip().split()
49 | L, R, M = int(L), int(R), int(M)
50 | total = TOTAL_OP[A](total, R//M-(L-1)//M)
51 | curr_hash = HASH_OP[A](curr_hash, get_sum(prefix, L, R, M))
52 | if total == B-1 and curr_hash in lookup: # check total to avoid hash collision
53 | result[i] = lookup[curr_hash]+1
54 | return " ".join(map(lambda x: str(x) if x else "X", result))
55 |
56 | seed(0)
57 | MAX_UINT64 = 2**64-1
58 | add = sub = xor
59 | TOTAL_OP = {'E':lambda a,b: a+b, 'D':lambda a, b:a-b}
60 | HASH_OP = {'E':add, 'D':sub}
61 | for case in xrange(input()):
62 | print 'Case #%d: %s' % (case+1, slide_circuits())
63 |
--------------------------------------------------------------------------------
/Round 1C/double_or_noting2.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 1C - Problem C. Double or NOTing
4 | # https://codingcompetitions.withgoogle.com/codejam/round/00000000004362d7/00000000007c1139
5 | #
6 | # Time: O(|E| + K * |S|), K is the number of bit groups of S
7 | # Space: O(|E| + |S|)
8 | #
9 |
10 | # from re import match
11 | from collections import deque
12 |
13 | def logical_flip(s, flag):
14 | while s and s[0]^flag == 1:
15 | s.popleft()
16 | if not s:
17 | s.append(0^(1^flag))
18 |
19 | def compare(s, e, flag):
20 | if len(s) > len(e):
21 | return False
22 | for i in xrange(len(s)):
23 | if s[i]^flag != e[i]:
24 | return False
25 | return True
26 |
27 | def init_flip_count(E):
28 | s = list(E)+[0] # if s ends with '1', it requires one more "not" operation (flip), which could be easily counted by appending a '0'
29 | suffix_flip_cnt = [0]*len(s)
30 | for i in reversed(xrange(len(s)-1)):
31 | suffix_flip_cnt[i] = suffix_flip_cnt[i+1] + int(s[i] != s[i+1])
32 | return suffix_flip_cnt
33 |
34 | def get_flip_count(suffix_flip_cnt, i):
35 | return suffix_flip_cnt[i] if i < len(suffix_flip_cnt) else 0
36 |
37 | def find_prefix_and_count(S, E, suffix_flip_cnt):
38 | result = float("inf")
39 | X = 0
40 | while S[0] != 0^(X%2):
41 | if compare(S, E, X%2) and X >= get_flip_count(suffix_flip_cnt, len(S)):
42 | return X+(len(E)-len(S)), None
43 | logical_flip(S, X%2)
44 | X += 1
45 | return result, X
46 |
47 | def double_or_noting():
48 | S, E = map(lambda x: deque(int(c) for c in list(x)), raw_input().strip().split())
49 |
50 | suffix_flip_cnt = init_flip_count(E)
51 | result, X = find_prefix_and_count(S, E, suffix_flip_cnt)
52 | if result != float("inf"):
53 | return result
54 | if X >= get_flip_count(suffix_flip_cnt, 0):
55 | return X+len(E)-int(E[0] == 0)
56 | cnt = get_flip_count(suffix_flip_cnt, 1)
57 | if cnt == 0:
58 | # assert(match("^10*$", E))
59 | return X+1+(len(E)-1) # S =X=> "0" =1=> "1" =(len(E)-1)=> "10*"
60 | if cnt == 1:
61 | # assert(match("^11+0*$", E))
62 | return X+1+len(E)+1 # S =X=> "0" =1=> "1" =k=> "100+" =1=> "11+" =(len(E)-k)=> "11+0*", where 2 <= k <= len(E)
63 | return "IMPOSSIBLE"
64 |
65 | for case in xrange(input()):
66 | print 'Case #%d: %s' % (case+1, double_or_noting())
67 |
--------------------------------------------------------------------------------
/Round 1A/prime_time.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 1A - Problem B. Prime Time
4 | # https://codingcompetitions.withgoogle.com/codejam/round/000000000043585d/00000000007543d8
5 | #
6 | # Time: O((MAX_P * logX) * (M + logX)), X is the sum of all cards
7 | # Space: O(1)
8 | #
9 |
10 | from collections import OrderedDict
11 |
12 | def max_card_number_of_group2(X, count): # Time: O(logX)
13 | # return (X-1).bit_length() # ceil_log2_X, estimated max_card_number_of_group2
14 | result, prod = 0, 1
15 | for p in count.iterkeys(): # use count.iterkeys() instead of count.iteritems() to avoid TLE
16 | for _ in xrange(count[p]):
17 | if prod*p > X:
18 | return result
19 | prod *= p
20 | result += 1
21 | return result
22 |
23 | def estimated_max_card_sum_of_group2(X, count): # Time: O(logX)
24 | # return max_card_number_of_group2(X, count) * next(reversed(count)) # less accurate estimated_max_card_sum_of_group2
25 | result, remain = 0, max_card_number_of_group2(X, count) # Time: O(logX)
26 | for p in reversed(count):
27 | result += p*min(remain, count[p])
28 | remain -= min(remain, count[p])
29 | if remain == 0:
30 | break
31 | return result # more accurate but may not be the lower bound of max_card_sum_of_group2
32 |
33 | # given prod = p1*p2*...*pk, check if
34 | # (1) p1+p2+...+pk = total
35 | # (2) numbers of p1,p2,...pk are within the given count limit
36 | def check(prod, total, count): # Time: O(M + logX)
37 | for p in count.iterkeys(): # use count.iterkeys() instead of count.iteritems() to avoid TLE
38 | for _ in xrange(count[p]):
39 | if prod%p != 0:
40 | break
41 | prod //= p # at most O(logX) times
42 | total -= p
43 | if total < 0: # early return
44 | return False
45 | return prod == 1 and total == 0
46 |
47 | def prime_time():
48 | M = input()
49 | count = OrderedDict()
50 | for _ in xrange(M):
51 | P, N = map(int, raw_input().strip().split())
52 | count[P] = N
53 | X = sum(p*n for p, n in count.iteritems())
54 |
55 | for i in xrange(1, estimated_max_card_sum_of_group2(X, count)+1): # prune impossible i
56 | if check(X-i, i, count):
57 | return X-i
58 | return 0
59 |
60 | for case in xrange(input()):
61 | print 'Case #%d: %s' % (case+1, prime_time())
62 |
--------------------------------------------------------------------------------
/Virtual World Finals/ropes.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Virtual World Finals - Problem C. Ropes
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000436329/000000000084fad0
5 | #
6 | # Time: O(N^3), pass in PyPy2 but Python2
7 | # Space: O(N^2)
8 | #
9 | # Usage: python interactive_runner.py python3 testing_tool.py 2 -- python ropes.py
10 | #
11 | # push it east solution
12 | # - Z=10, 18322/20000 = 91.610 %
13 | # - Z=14, 18251/20000 = 91.255 %
14 | #
15 |
16 | from sys import stdout
17 | from itertools import imap
18 |
19 | def play(i, j):
20 | print i+1, j+1 # 0-indexed
21 | stdout.flush()
22 | return map(lambda x: int(x)-1, raw_input().strip().split()) # 0-indexed
23 |
24 | def check_result(A_score, B_score):
25 | assert(input() == int(A_score > B_score))
26 |
27 | def greedy(score_matrix):
28 | # given that opposing team choose the play that yields the maximum possible score for this turn.
29 | # if there is a tie:
30 | # - they will choose one at random as problem description claims
31 | # - they will not choose by a counter-greedy strategy (they choose as far east as possible), since we can never win by this greedy strategy if they can always do that
32 | result = None
33 | best_score = max(imap(max, score_matrix))
34 | for i in xrange(2*N):
35 | for j in xrange(2*N):
36 | if score_matrix[i][j] == best_score and (result is None or (sum(result) > i+j)): # we choose as far west as possible if there is a tie
37 | result = (i, j)
38 | return result
39 |
40 | def update(i, j, score_matrix):
41 | for r in xrange(2*N):
42 | for c in xrange(2*N):
43 | if r == i or c == j:
44 | score_matrix[r][c] = NEG_INF
45 | elif (r-i)*(c-j) < 0:
46 | score_matrix[r][c] += 1
47 |
48 | def ropes():
49 | score_matrix = [[0 for _ in xrange(2*N)] for _ in xrange(2*N)]
50 | A_score = B_score = 0
51 | for k in xrange(N):
52 | i, j = greedy(score_matrix) if k else (Z-1, Z-1) # tuned by experitments that i=j at the first play is better
53 | A_score += score_matrix[i][j]
54 | update(i, j, score_matrix)
55 | i, j = play(i, j)
56 | B_score += score_matrix[i][j]
57 | update(i, j, score_matrix)
58 | check_result(A_score, B_score)
59 |
60 | NEG_INF = float("-inf")
61 | Z = 10 # tuned by experiments that top 4 win rates are with Z = [10, 14, 9, 11]
62 | T, N, W = map(int, raw_input().strip().split())
63 | for case in xrange(T):
64 | ropes()
65 |
--------------------------------------------------------------------------------
/Round 2/retiling.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 2 - Problem D. Retiling
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000435915/00000000007dc2de
5 | #
6 | # Time: O((R * C)^3)
7 | # Space: O((R * C)^2)
8 | #
9 |
10 | # Template translated from:
11 | # https://github.com/kth-competitive-programming/kactl/blob/main/content/graph/WeightedMatching.h
12 | # Time: O(N^2 * M)
13 | # Space: O(N + M)
14 | def hungarian(a):
15 | if not a:
16 | return 0, []
17 | n, m = len(a)+1, len(a[0])+1
18 | u, v, p, ans = [0]*n, [0]*m, [0]*m, [0]*(n-1)
19 | for i in xrange(1, n):
20 | p[0] = i
21 | j0 = 0 # add "dummy" worker 0
22 | dist, pre = [float("inf")]*m, [-1]*m
23 | done = [False]*(m+1)
24 | while True: # dijkstra
25 | done[j0] = True
26 | i0, j1, delta = p[j0], None, float("inf")
27 | for j in xrange(1, m):
28 | if done[j]:
29 | continue
30 | cur = a[i0-1][j-1]-u[i0]-v[j]
31 | if cur < dist[j]:
32 | dist[j], pre[j] = cur, j0
33 | if dist[j] < delta:
34 | delta, j1 = dist[j], j
35 | for j in xrange(m):
36 | if done[j]:
37 | u[p[j]] += delta
38 | v[j] -= delta
39 | else:
40 | dist[j] -= delta
41 | j0 = j1
42 | if not p[j0]:
43 | break
44 | while j0: # update alternating path
45 | j1 = pre[j0]
46 | p[j0], j0 = p[j1], j1
47 | for j in xrange(1, m):
48 | if p[j]:
49 | ans[p[j]-1] = j-1
50 | return -v[0], ans # min cost
51 |
52 | def retiling():
53 | R, C, F, S = map(int, raw_input().strip().split())
54 | src, dst = [[raw_input().strip() for _ in xrange(R)] for _ in xrange(2)]
55 | pos0 = [(i, j) for i in xrange(R) for j in xrange(C) if src[i][j] == 'M']
56 | pos1 = [(i, j) for i in xrange(R) for j in xrange(C) if dst[i][j] == 'M']
57 | cost = [[0]*(len(pos0)+len(pos1)) for _ in xrange(len(pos0)+len(pos1))]
58 | for i in xrange(len(cost)):
59 | for j in xrange(len(cost[0])):
60 | if i < len(pos0) and j < len(pos1):
61 | cost[i][j] = S * (abs(pos0[i][0]-pos1[j][0])+abs(pos0[i][1]-pos1[j][1]))
62 | elif i < len(pos0) or j < len(pos1):
63 | cost[i][j] = F
64 | return hungarian(cost)[0]
65 |
66 | for case in xrange(input()):
67 | print 'Case #%d: %s' % (case+1, retiling())
68 |
--------------------------------------------------------------------------------
/Virtual World Finals/cutting_cake.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Virtual World Finals - Problem A. Cutting Cake
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000436329/000000000084fba1
5 | #
6 | # Time: O(NlogN)
7 | # Space: O(N)
8 | #
9 |
10 | from fractions import Fraction
11 |
12 | def ccw(A, B, C):
13 | return (B[0]-A[0])*(C[1]-A[1]) - (B[1]-A[1])*(C[0]-A[0])
14 |
15 | def find_delta_slopes(points):
16 | delta_slopes = [Fraction(0)]*3
17 | for i in xrange(len(points)):
18 | if not points[i][0]-points[i-1][0]:
19 | continue
20 | slope = Fraction(points[i][1]-points[i-1][1], points[i][0]-points[i-1][0])
21 | delta_slopes[i-1] += slope
22 | delta_slopes[i] -= slope
23 | assert(sum(delta_slopes) == 0)
24 | return delta_slopes
25 |
26 | def cutting_cake():
27 | N, W, H = map(int, raw_input().strip().split())
28 | P, Q, R, S = map(Fraction, raw_input().strip().split())
29 | points = sorted([(Fraction(0), Fraction(0)), (P, Q), (R, S)])
30 | prev_diff, area = Fraction(0), Fraction(-ccw(*points), 2)
31 | events = []
32 | for _ in xrange(N):
33 | X, Y, A, B = map(Fraction, raw_input().strip().split())
34 | prev_diff -= B*area
35 | for i in xrange(len(points)):
36 | events.append((X+points[i][0], i, A+B))
37 | events.sort()
38 |
39 | result = abs(prev_diff)
40 | delta_y = [points[i][1]-points[i-1][1] if points[i-1][0] == points[i][0] else 0 for i in xrange(len(points))]
41 | delta_slopes = find_delta_slopes(points)
42 | prev_x = prev_y = slope = Fraction(0)
43 | for curr_x, i, w in events:
44 | dx = curr_x-prev_x
45 | if not dx:
46 | prev_y += w*delta_y[i]
47 | slope += w*delta_slopes[i]
48 | continue
49 | curr_y = prev_y+dx*slope
50 | curr_diff = prev_diff+(prev_y+curr_y)/2*dx
51 | result = min(result, abs(curr_diff)) # the values at each endpoint of the interval
52 | extreme_diff = prev_diff
53 | if curr_y*prev_y < 0: # find the value at the extreme point of the quadratic if that's within the interval
54 | extreme_y = Fraction(0)
55 | extreme_x = (extreme_y-prev_y)/slope+prev_x
56 | extreme_diff = prev_diff+(prev_y+extreme_y)/2*(extreme_x-prev_x)
57 | result = min(result, abs(extreme_diff))
58 | if min(extreme_diff, prev_diff, curr_diff) <= 0 <= max(extreme_diff, prev_diff, curr_diff): # whether the quadratic crosses 0 within the interval
59 | result = Fraction(0)
60 | break
61 | prev_diff, prev_x, prev_y = curr_diff, curr_x, curr_y
62 | slope += w*delta_slopes[i]
63 | result /= abs(area)
64 | return "%s/%s"%(result.numerator, result.denominator)
65 |
66 | for case in xrange(input()):
67 | print 'Case #%d: %s' % (case+1, cutting_cake())
68 |
--------------------------------------------------------------------------------
/Round 1A/hacked_exam.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 1A - Problem C. Hacked Exam
4 | # https://codingcompetitions.withgoogle.com/codejam/round/000000000043585d/0000000000754750
5 | #
6 | # Time: precompute: O(MAX_Q^2)
7 | # runtime: O(Q)
8 | # Space: O(MAX_Q^2), for nCr cache
9 | #
10 |
11 | from itertools import izip
12 | from fractions import gcd
13 |
14 | def hacked_exam():
15 | N, Q = map(int, raw_input().strip().split())
16 | A, S = [], []
17 | for _ in xrange(N):
18 | a, s = raw_input().strip().split()
19 | A.append(a)
20 | S.append(int(s))
21 | while N < 3: # duplicate until N = 3
22 | A.append(A[-1])
23 | S.append(S[-1])
24 | N += 1
25 | for _ in xrange(len(nCr), Q+1): # cached nCr, O(MAX_Q^2) time in total
26 | nCr.append([1] + [nCr[-1][i] + nCr[-1][i+1] for i in xrange(len(nCr[-1])-1)] + [1])
27 |
28 | an = sum(a == b == c for a, b, c in izip(*A))
29 | bn = sum(b == c != a for a, b, c in izip(*A))
30 | cn = sum(c == a != b for a, b, c in izip(*A))
31 | dn = sum(a == b != c for a, b, c in izip(*A))
32 | total = acount = bcount = ccount = dcount = 0
33 | for ar in xrange(an+1):
34 | # (1) ar + (bn-br) + cr + dr = S[0]
35 | # (2) ar + br + (cn-cr) + dr = S[1]
36 | # (3) ar + br + cr + (dn-dr) = S[2]
37 | br = (S[1]+S[2]-cn-dn)//2-ar # [(2)+(3)]/2, since at least one br exists and (S[1]+S[2]-cn-dn)//2 is constant, so (S[1]+S[2]-cn-dn)%2 is always 0
38 | cr = (S[2]+S[0]-bn-dn)//2-ar # [(3)+(1)]/2, since at least one cr exists and (S[2]+S[0]-bn-dn)//2 is constant, so (S[2]+S[0]-bn-dn)%2 is always 0
39 | dr = (S[0]+S[1]-bn-cn)//2-ar # [(1)+(2)]/2, since at least one dr exists and (S[0]+S[1]-bn-cn)//2 is constant, so (S[0]+S[1]-bn-cn)%2 is always 0
40 | if not (0 <= br <= bn and 0 <= cr <= cn and 0 <= dr <= dn):
41 | continue # ar is invalid
42 | ways = nCr[an][ar] * nCr[bn][br] * nCr[cn][cr] * nCr[dn][dr]
43 | total += ways
44 | acount += ways*ar
45 | bcount += ways*br
46 | ccount += ways*cr
47 | dcount += ways*dr
48 | result = []
49 | for a, b, c in izip(*A):
50 | if a == b == c:
51 | result.append(a if acount >= total*an-acount else "TF"[a == 'T'])
52 | elif b == c != a:
53 | result.append(b if bcount >= total*bn-bcount else a)
54 | elif c == a != b:
55 | result.append(c if ccount >= total*cn-ccount else b)
56 | elif a == b != c:
57 | result.append(a if dcount >= total*dn-dcount else c)
58 | count = max(acount, total*an-acount) + max(bcount, total*bn-bcount) + max(ccount, total*cn-ccount) + max(dcount, total*dn-dcount)
59 | g = gcd(count, total)
60 | return "%s %s/%s" % ("".join(result), count//g, total//g)
61 |
62 | nCr = [[1]]
63 | for case in xrange(input()):
64 | print 'Case #%d: %s' % (case+1, hacked_exam())
65 |
--------------------------------------------------------------------------------
/Round 1B/digit_blocks.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 1B - Problem C. Digit Blocks
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000435baf/00000000007ae37b
5 | #
6 | # Time: precompute: O(N^3 * B * D)
7 | # runtime: O(N * B)
8 | # Space: O(N^3 * B * D)
9 | #
10 | # Usage: python interactive_runner.py python3 testing_tool.py 1 -- python digit_blocks.py
11 | #
12 | # Expected score compared to the max expected score = 19086952424670896.00/19131995794056374.42 = 99.76%
13 | #
14 |
15 | from sys import stdout
16 |
17 | def read():
18 | return input()
19 |
20 | def write(i):
21 | print i
22 | stdout.flush()
23 |
24 | def digit_blocks():
25 | grow_h = 0
26 | lookup = [[] for _ in xrange(B+1)]
27 | lookup[0] = range(N)
28 | for _ in xrange(N*B):
29 | d = read()
30 | h = choice[len(lookup[B])][len(lookup[B-1])][len(lookup[B-2])][grow_h][d]
31 | if h < B-2:
32 | grow_h = (grow_h+1)%(B-2)
33 | lookup[h+1].append(lookup[h].pop())
34 | write(lookup[h+1][-1]+1)
35 |
36 | D = 10
37 | T, N, B, P = map(int, raw_input().strip().split())
38 | P = [1]
39 | while len(P) < B:
40 | P.append(P[-1]*D)
41 | dp = [[[[0.0 for _ in xrange(B-2)] for _ in xrange(N+1)] for _ in xrange(N+1)] for _ in xrange(N+1)]
42 | choice = [[[[[None for _ in xrange(D)] for _ in xrange(B-2)] for _ in xrange(N+1)] for _ in xrange(N+1)] for _ in xrange(N+1)]
43 | for remain0_cnt in reversed(xrange(N)):
44 | for remain1_cnt in reversed(xrange(N-remain0_cnt+1)):
45 | for remain2_cnt in reversed(xrange(N-remain0_cnt-remain1_cnt+1)):
46 | for grow_h in reversed(xrange(1 if remain0_cnt+remain1_cnt+remain2_cnt == N else B-2)):
47 | for d in xrange(D):
48 | max_ev = float("-inf")
49 | if remain1_cnt:
50 | ev = dp[remain0_cnt+1][remain1_cnt-1][remain2_cnt][grow_h] + P[B-1]*d
51 | if ev > max_ev:
52 | max_ev = ev
53 | choice[remain0_cnt][remain1_cnt][remain2_cnt][grow_h][d] = B-1
54 | if remain2_cnt:
55 | ev = dp[remain0_cnt][remain1_cnt+1][remain2_cnt-1][grow_h] + P[B-2]*d
56 | if ev > max_ev:
57 | max_ev = ev
58 | choice[remain0_cnt][remain1_cnt][remain2_cnt][grow_h][d] = B-2
59 | if remain0_cnt+remain1_cnt+remain2_cnt != N:
60 | ev = dp[remain0_cnt][remain1_cnt][remain2_cnt+(grow_h+1)//(B-2)][(grow_h+1)%(B-2)] + P[grow_h]*d
61 | if ev > max_ev:
62 | max_ev = ev
63 | choice[remain0_cnt][remain1_cnt][remain2_cnt][grow_h][d] = grow_h
64 | dp[remain0_cnt][remain1_cnt][remain2_cnt][grow_h] += max_ev/D
65 | S = 19131995794056374.42
66 | assert(dp[0][0][0][0]/S >= 0.9976)
67 | for case in xrange(T):
68 | digit_blocks()
69 |
--------------------------------------------------------------------------------
/Round 3/build_a_pair3.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 3 - Problem A. Build-A-Pair
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000436142/0000000000813aa8
5 | #
6 | # Time: O(b^2 * N), b = 10
7 | # Space: O(b)
8 | #
9 | # optimized from build_a_pair2.py
10 | #
11 |
12 | def greedy(n, l, count, dir): # Time: O(N)
13 | for i in dir(xrange(len(count))):
14 | if count[i] == 0:
15 | continue
16 | common = min(l, count[i])
17 | l -= common
18 | count[i] -= common
19 | for _ in xrange(common):
20 | n = 10*n + i
21 | return n
22 |
23 | def odd_case(count): # Time: O(N)
24 | d = next(d for d in xrange(1, len(count)) if count[d])
25 | count[d] -= 1
26 | remain = sum(count)
27 | A = greedy(d, remain//2, count, lambda x: x)
28 | B = greedy(0, remain//2, count, reversed)
29 | return A-B
30 |
31 | def even_case(count): # Time: O(b^2 * N)
32 | result = float("inf")
33 | for d in xrange(-1, len(count)): # other than the shared prefix, there is no digit with any pair, or a digit with 1 pair, or digit 0 with all pairs, O(b) times
34 | if d != -1 and count[d] < 2:
35 | continue
36 | new_counts = [[count[j]%2 if j != d else (2+count[d]%2 if i != 1 else count[d]) for j in xrange(len(count))] for i in xrange(2 if d == 0 else 1)]
37 | for new_count in new_counts: # at most O(2) times
38 | has_prefix = True
39 | if all(new_count[k] == count[k] for k in xrange(1, len(count))): # no digit other than 0 is chosen, O(b) times
40 | if new_count[0] != count[0]: # invalid
41 | continue
42 | has_prefix = False
43 | candidates = [k for k, v in enumerate(new_count) if v and (k or has_prefix)]
44 | if not candidates:
45 | return 0
46 | if len(candidates) == 1:
47 | continue
48 | remain = sum(new_count)
49 | min_diff = min(candidates[i]-candidates[i-1] for i in xrange(1, len(candidates)))
50 | for i in xrange(1, len(candidates)): # O(b) times
51 | a, b = candidates[i], candidates[i-1]
52 | if new_count[b] == 0 or a-b != min_diff: # for each a, b s.t. a-b > min_diff, where any A-B won't be the result
53 | continue
54 | tmp_count = new_count[:]
55 | tmp_count[a] -= 1
56 | tmp_count[b] -= 1
57 | A = greedy(a, remain//2-1, tmp_count, lambda x: x) # Time: O(N)
58 | B = greedy(b, remain//2-1, tmp_count, reversed) # Time: O(N)
59 | result = min(result, A-B)
60 | return result
61 |
62 | def build_a_pair():
63 | D = map(int, list(raw_input().strip()))
64 |
65 | count = [0]*BASE
66 | for c in D:
67 | count[int(c)] += 1
68 | return odd_case(count) if sum(count)%2 == 1 else even_case(count)
69 |
70 | BASE = 10
71 | for case in xrange(input()):
72 | print 'Case #%d: %s' % (case+1, build_a_pair())
73 |
--------------------------------------------------------------------------------
/Virtual World Finals/divisible_divisions.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Virtual World Finals - Problem D. Divisible Divisions
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000436329/000000000084fb3a
5 | #
6 | # Time: O(|S|logD + D)
7 | # Space: O(|S| + D)
8 | #
9 |
10 | from collections import Counter
11 |
12 | def addmod(a, b):
13 | return (a+b)%MOD
14 |
15 | def divisible_divisions():
16 | S, D = raw_input().strip().split()
17 | S, D = map(int, list(S)), int(D)
18 |
19 | cnts = Counter([1])
20 | d_remain = D
21 | for p in [2, 5]:
22 | while d_remain%p == 0:
23 | d_remain //= p
24 | cnts[p] += 1
25 | l = max(cnts.itervalues()) # l = O(logD)
26 |
27 | suffix = [0]*(len(S)+1)
28 | basis = 1
29 | for i in reversed(xrange(len(S))):
30 | suffix[i] = (suffix[i+1] + S[i]*basis) % d_remain
31 | basis = basis*10 % d_remain
32 |
33 | # dp1[i]: count of divisible divisions of this prefix whose last division is divisible by D ends at S[i-1]
34 | # dp2[i]: count of divisible divisions of this prefix whose last division is not divisible by D ends at S[i-1]
35 | dp1, dp2 = [[0]*(len(S)+1) for _ in xrange(2)]
36 | dp1[0] = 1
37 | prefix_total, prefix_dp1 = [[0]*d_remain for _ in xrange(2)]
38 | accu_dp1, d_2_5 = 1, D//d_remain
39 | for i in xrange(1, len(S)+1):
40 | dp2[i] = accu_dp1
41 | curr, basis = 0, 1
42 | for k in xrange(1, l+1): # O(logD) times
43 | if i-k < 0:
44 | break
45 | j = i-k
46 | curr = (curr + S[j]*basis) % d_2_5
47 | if k == l:
48 | prefix_total[suffix[j]] = addmod(prefix_total[suffix[j]], addmod(dp1[j], dp2[j]))
49 | prefix_dp1[suffix[j]] = addmod(prefix_dp1[suffix[j]], dp1[j])
50 | if curr == 0:
51 | # since all(S[j:i]%d_2_5 == 0 for j in xrange(i-l+1)) is true,
52 | # find sum(cnt[j] for j in xrange(i-l+1) if suffix[j] == suffix[i]) <=> find sum(cnt[j] for j in xrange(i-l+1) if S[j:i]%D == 0)
53 | dp1[i] = addmod(dp1[i], prefix_total[suffix[i]]) # prefix_total[suffix[i]] = sum(dp1[j]+dp2[j] for j in xrange(i-l+1) if suffix[j] == suffix[i])%MOD
54 | dp2[i] = addmod(dp2[i], -prefix_dp1[suffix[i]]) # prefix_dp1[suffix[i]] = sum(dp1[j] for j in xrange(i-l+1) if suffix[j] == suffix[i])%MOD
55 | break
56 | if curr == 0 and suffix[j] == suffix[i]: # (S[j:i]%d_2_5 == 0) and (suffix[j]-suffix[i] == 0 <=> (S[j:i]*10^(len(S)-i)%d_remain == 0 and gcd(d_remain, 10^(len(S)-i)) == 1) <=> S[j:i]%d_remain == 0) <=> S[j:i]%D == 0
57 | dp1[i] = addmod(dp1[i], addmod(dp1[j], dp2[j]))
58 | dp2[i] = addmod(dp2[i], -dp1[j])
59 | basis = basis*10 % d_2_5
60 | accu_dp1 = addmod(accu_dp1, dp1[i])
61 | return addmod(dp1[len(S)], dp2[len(S)])
62 |
63 | MOD = 10**9+7
64 | for case in xrange(input()):
65 | print 'Case #%d: %s' % (case+1, divisible_divisions())
66 |
--------------------------------------------------------------------------------
/Round 3/build_a_pair.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 3 - Problem A. Build-A-Pair
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000436142/0000000000813aa8
5 | #
6 | # Time: O((N/(2b) + 1)^b * b^2 * N), b = 10, pass in PyPy2 but Python2
7 | # Space: O(b)
8 | #
9 |
10 | from operator import mul
11 |
12 | def greedy(n, l, count, dir): # Time: O(N)
13 | for i in dir(xrange(len(count))):
14 | if count[i] == 0:
15 | continue
16 | common = min(l, count[i])
17 | l -= common
18 | count[i] -= common
19 | for _ in xrange(common):
20 | n = 10*n + i
21 | return n
22 |
23 | def odd_case(count): # Time: O(N)
24 | d = next(d for d in xrange(1, len(count)) if count[d])
25 | count[d] -= 1
26 | remain = sum(count)
27 | A = greedy(d, remain//2, count, lambda x: x)
28 | B = greedy(0, remain//2, count, reversed)
29 | return A-B
30 |
31 | def mask_to_count(count, choice, mask): # Time: O(b)
32 | new_count = [0]*BASE
33 | for k, v in enumerate(choice):
34 | mask, cnt = divmod(mask, v)
35 | new_count[k] = cnt*2+count[k]%2
36 | return new_count
37 |
38 | def even_case(count): # Time: O((N/(2b) + 1)^b * b^2 * N)
39 | choice = [0]*BASE
40 | for k, v in enumerate(count):
41 | choice[k] = v//2+1
42 | total = reduce(mul, (v for v in choice if v))
43 | result = float("inf")
44 | for mask in xrange(total): # enumerate all possible prefixes
45 | # N/2 + b >= (c0+1) + (c1+1) + ... + (c(b-1)+1) >= b * ((c0+1)*(c1+1)*...*(c(b-1)+1))^(1/b)
46 | # (c0+1)*(c1+1)*...*(c(b-1)+1) <= (N/(2b) + 1)^b
47 | # mask loops at most O((N/(2b) + 1)^b) times
48 | has_prefix = True
49 | new_count = mask_to_count(count, choice, mask)
50 | if all(new_count[k] == count[k] for k in xrange(1, len(count))): # no digit other than 0 is chosen
51 | if new_count[0] != count[0]: # invalid
52 | continue
53 | has_prefix = False
54 | candidates = [k for k, v in enumerate(new_count) if v and (k or has_prefix)]
55 | if not candidates:
56 | return 0
57 | if len(candidates) == 1:
58 | continue
59 | remain = sum(new_count)
60 | for i in xrange(1, len(candidates)): # O(b^2) times
61 | for j in xrange(i):
62 | tmp_count = new_count[:]
63 | tmp_count[candidates[i]] -= 1
64 | tmp_count[candidates[j]] -= 1
65 | A = greedy(candidates[i], remain//2-1, tmp_count, lambda x: x) # Time: O(N)
66 | B = greedy(candidates[j], remain//2-1, tmp_count, reversed) # Time: O(N)
67 | result = min(result, A-B)
68 | return result
69 |
70 | def build_a_pair():
71 | D = map(int, list(raw_input().strip()))
72 |
73 | count = [0]*BASE
74 | for c in D:
75 | count[int(c)] += 1
76 | return odd_case(count) if sum(count)%2 == 1 else even_case(count)
77 |
78 | BASE = 10
79 | for case in xrange(input()):
80 | print 'Case #%d: %s' % (case+1, build_a_pair())
81 |
--------------------------------------------------------------------------------
/Round 1C/double_or_noting3.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 1C - Problem C. Double or NOTing
4 | # https://codingcompetitions.withgoogle.com/codejam/round/00000000004362d7/00000000007c1139
5 | #
6 | # Time: O(|E| + |S|)
7 | # Space: O(|E| + |S|)
8 | #
9 | # KMP solution
10 | #
11 |
12 | # from re import match
13 |
14 | def modified_KMP(text, pattern, prefix, flag):
15 | j = -1
16 | for i in xrange(len(text)):
17 | while j != -1 and pattern[j+1] != text[i]^flag:
18 | j = prefix[j]
19 | if pattern[j+1] == text[i]^flag:
20 | j += 1
21 | if j+1 == len(pattern):
22 | if i != len(text)-1:
23 | j = prefix[j]
24 | return j # pattern[:j+1] is the longest suffix of text
25 |
26 | def get_prefix(pattern):
27 | prefix = [-1]*len(pattern)
28 | j = -1
29 | for i in xrange(1, len(pattern)):
30 | while j != -1 and pattern[j+1] != pattern[i]:
31 | j = prefix[j]
32 | if pattern[j+1] == pattern[i]:
33 | j += 1
34 | prefix[i] = j
35 | return prefix
36 |
37 | def init_flip_count(E):
38 | s = E+[0] # if s ends with '1', it requires one more "not" operation (flip), which could be easily counted by appending a '0'
39 | suffix_flip_cnt = [0]*len(s)
40 | for i in reversed(xrange(len(s)-1)):
41 | suffix_flip_cnt[i] = suffix_flip_cnt[i+1]+int(s[i] != s[i+1])
42 | return suffix_flip_cnt
43 |
44 | def get_flip_count(suffix_flip_cnt, i):
45 | return suffix_flip_cnt[i] if i < len(suffix_flip_cnt) else 0
46 |
47 | def find_X(S):
48 | X_cnt = [-1]*len(S) # only valid positions with X >= 0
49 | X_cnt[0] = X = 0
50 | for i in xrange(1, len(S)):
51 | if S[i] == S[i-1]:
52 | continue
53 | X += 1
54 | X_cnt[i] = X
55 | return X_cnt, X+S[0]
56 |
57 | def find_prefix_and_count(S, E, suffix_flip_cnt):
58 | result = float("inf")
59 | X_cnt, X = find_X(S)
60 | if 0 in [S[0], E[0]]:
61 | return result, X
62 | prefix = get_prefix(E)
63 | for i in xrange(2):
64 | j = modified_KMP(S, E, prefix, i)
65 | while j != -1:
66 | if X_cnt[-1-j] >= get_flip_count(suffix_flip_cnt, j+1): # S[-1-j:] is the same structure as E[:j+1] and its X >= flip_cnt(E[j+1:])
67 | result = min(result, X_cnt[-1-j]+(len(E)-(j+1)))
68 | break
69 | j = prefix[j]
70 | return result, X
71 |
72 | def double_or_noting():
73 | S, E = map(lambda x: [int(c) for c in list(x)], raw_input().strip().split())
74 |
75 | suffix_flip_cnt = init_flip_count(E)
76 | result, X = find_prefix_and_count(S, E, suffix_flip_cnt)
77 | if result != float("inf"):
78 | return result
79 | if X >= get_flip_count(suffix_flip_cnt, 0):
80 | return X+len(E)-int(E[0] == 0)
81 | cnt = get_flip_count(suffix_flip_cnt, 1)
82 | if cnt == 0:
83 | # assert(match("^10*$", E))
84 | return X+1+(len(E)-1) # S =X=> "0" =1=> "1" =(len(E)-1)=> "10*"
85 | if cnt == 1:
86 | # assert(match("^11+0*$", E))
87 | return X+1+len(E)+1 # S =X=> "0" =1=> "1" =k=> "100+" =1=> "11+" =(len(E)-k)=> "11+0*", where 2 <= k <= len(E)
88 | return "IMPOSSIBLE"
89 |
90 | for case in xrange(input()):
91 | print 'Case #%d: %s' % (case+1, double_or_noting())
92 |
--------------------------------------------------------------------------------
/Round 3/build_a_pair2.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 3 - Problem A. Build-A-Pair
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000436142/0000000000813aa8
5 | #
6 | # Time: O(3^b * b * N), b = 10, pass in PyPy2 but Python2
7 | # Space: O(b)
8 | #
9 | # optimized from build_a_pair.py
10 | #
11 |
12 | from operator import mul
13 |
14 | def greedy(n, l, count, dir): # Time: O(N)
15 | for i in dir(xrange(len(count))):
16 | if count[i] == 0:
17 | continue
18 | common = min(l, count[i])
19 | l -= common
20 | count[i] -= common
21 | for _ in xrange(common):
22 | n = 10*n + i
23 | return n
24 |
25 | def odd_case(count): # Time: O(N)
26 | d = next(d for d in xrange(1, len(count)) if count[d])
27 | count[d] -= 1
28 | remain = sum(count)
29 | A = greedy(d, remain//2, count, lambda x: x)
30 | B = greedy(0, remain//2, count, reversed)
31 | return A-B
32 |
33 | def mask_to_count(count, choice, mask): # Time: O(b)
34 | new_count = [0]*BASE
35 | for k, v in enumerate(choice):
36 | mask, cnt = divmod(mask, v)
37 | new_count[k] = cnt*2+count[k]%2 if cnt != CHOICE-1 else count[k]
38 | return new_count
39 |
40 | def even_case(count): # Time: O(3^b * b * N)
41 | choice = [0]*BASE
42 | for k, v in enumerate(count):
43 | choice[k] = min(v//2+1, CHOICE)
44 | total = reduce(mul, (v for v in choice if v))
45 | result = float("inf")
46 | for mask in xrange(total): # enumerate all possible prefixes, loops O(3^b) times
47 | has_prefix = True
48 | new_count = mask_to_count(count, choice, mask)
49 | if all(new_count[k] == count[k] for k in xrange(1, len(count))): # no digit other than 0 is chosen
50 | if new_count[0] != count[0]: # invalid
51 | continue
52 | has_prefix = False
53 | candidates = [k for k, v in enumerate(new_count) if v and (k or has_prefix)]
54 | if not candidates:
55 | return 0
56 | if len(candidates) == 1:
57 | continue
58 | remain = sum(new_count)
59 | min_diff = min(candidates[i]-candidates[i-1] for i in xrange(1, len(candidates)))
60 | for i in xrange(1, len(candidates)): # O(b) times
61 | a, b = candidates[i], candidates[i-1]
62 | if new_count[b] == 0 or a-b != min_diff: # for each a, b s.t. a-b > min_diff, where any A-B won't be the result
63 | continue
64 | tmp_count = new_count[:]
65 | tmp_count[a] -= 1
66 | tmp_count[b] -= 1
67 | A = greedy(a, remain//2-1, tmp_count, lambda x: x) # Time: O(N)
68 | B = greedy(b, remain//2-1, tmp_count, reversed) # Time: O(N)
69 | result = min(result, A-B)
70 | return result
71 |
72 | def build_a_pair():
73 | D = map(int, list(raw_input().strip()))
74 |
75 | count = [0]*BASE
76 | for c in D:
77 | count[int(c)] += 1
78 | return odd_case(count) if sum(count)%2 == 1 else even_case(count)
79 |
80 | CHOICE = 3 # other than the shared prefix, since keeping 1 pair may happen in some cases, for each digit, we have 3 choices to keep: 0 pair, 1 pair, all pairs
81 | BASE = 10
82 | for case in xrange(input()):
83 | print 'Case #%d: %s' % (case+1, build_a_pair())
84 |
--------------------------------------------------------------------------------
/Round 3/square_free.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 3 - Problem B. Square Free
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000436142/0000000000813e1a
5 | #
6 | # Time: O(R^2 * C^2)
7 | # Space: O(R + C)
8 | #
9 |
10 | def inplace_counting_sort(nums, reverse=False): # Time: O(len(nums)+max(nums)), Space: O(max(nums))
11 | count = [0]*(max(nums)+1)
12 | for num in nums:
13 | count[num] += 1
14 | for i in xrange(1, len(count)):
15 | count[i] += count[i-1]
16 | for i in reversed(xrange(len(nums))): # inplace but unstable sort
17 | if nums[i] < 0: # processed
18 | continue
19 | while i != count[nums[i]]-1:
20 | count[nums[i]] -= 1
21 | nums[count[nums[i]]], nums[i] = ~nums[i], nums[count[nums[i]]]
22 | count[nums[i]] -= 1
23 | nums[i] = ~nums[i]
24 | for i in xrange(len(nums)):
25 | nums[i] = ~nums[i] # restore values
26 | if reverse: # unstable sort
27 | nums.reverse()
28 |
29 | def possible(S, D): # Time: O(R * C), Space: O(R + C)
30 | inplace_counting_sort(S, reverse=True) # Time: O(R + C), Space: O(C)
31 | inplace_counting_sort(D, reverse=True) # Time: O(R + C), Space: O(R)
32 | S_prefix = [0]
33 | for i in xrange(len(S)): # Time: O(R), Space: O(R)
34 | S_prefix.append(S_prefix[-1] + S[i])
35 | D_suffix = [0]
36 | for i in reversed(xrange(len(D))): # Time: O(C), Space: O(C)
37 | D_suffix.append(D_suffix[-1] + D[i])
38 | D_suffix.reverse()
39 | # consider a graph running max flow algorithm where edge from source to each Sx is with capacity S[x], edge from each Sx to each Dy is with capacity 1, edge from each Dy to sink is with capacity D[y],
40 | # if sum(S) != sum(D), it is impossible,
41 | # otherwise, we want all nodes with full capacity,
42 | # it is possible
43 | # <=> sum(S[x] for x in X)-sum(D[y] for y in Y) <= |X|*(C-|Y|) for all 0 <= |X| <= R and 0 <= |Y| <= C
44 | # <=> sum(S[x] for x in X')-sum(D[y] for y in Y') <= |X|*|Y| for all 0 <= |X| <= R and 0 <= |Y| <= C
45 | # and X' is the biggist |X| of S and Y' is the smallest C-|Y| of D
46 | # <=> -(sum(S)-sum(S[x] for x in X'))+(sum(D)-sum(D[y]) for y in Y') <= |X|*|Y| for all 0 <= |X| <= R and 0 <= |Y| <= C
47 | # and X' is the biggist |X| of S and Y' is the smallest C-|Y| of D
48 | # <=> sum(D[y] for y in Y'')-sum(S[x] for x in X'') <= |X|*|Y| for all 0 <= |X| <= R and 0 <= |Y| <= C
49 | # and Y'' is the biggest |Y| of D and X'' is the smallest R-|X| of S
50 | return S_prefix[-1] == D_suffix[0] and \
51 | all(S_prefix[i]-D_suffix[j] <= i*j for i in xrange(len(S_prefix)) for j in xrange(len(D_suffix))) # Time: O(R * C)
52 |
53 | def square_free():
54 | R, C = map(int, raw_input().strip().split())
55 | S = map(lambda x: C-int(x), raw_input().strip().split())
56 | D = map(lambda x: R-int(x), raw_input().strip().split())
57 |
58 | if not possible(S[:], D[:]):
59 | return "IMPOSSIBLE"
60 | result = [['/']*C for _ in xrange(R)]
61 | for i in xrange(R):
62 | for j in xrange(C):
63 | if not (S[i] >= 1 and D[j] >= 1 and possible([S[k]-int(k == i) for k in xrange(len(S))], [D[k]-int(k == j) for k in xrange(len(D))])):
64 | continue
65 | result[i][j] = '\\' # lexicographically smallest, assumed '\\' < '/'
66 | S[i], D[j] = S[i]-1, D[j]-1
67 | return "POSSIBLE\n"+"\n".join("".join(row) for row in result)
68 |
69 | for case in xrange(input()):
70 | print 'Case #%d: %s' % (case+1, square_free())
71 |
--------------------------------------------------------------------------------
/Virtual World Finals/divisible_divisions2.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Virtual World Finals - Problem D. Divisible Divisions
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000436329/000000000084fb3a
5 | #
6 | # Time: O(|S|logD)
7 | # Space: O(min(|S|logD, D))
8 | #
9 | # Time and Space optimized from divisible_divisions.py
10 | #
11 |
12 | from collections import Counter
13 |
14 | def addmod(a, b):
15 | return (a+b)%MOD
16 |
17 | # Template:
18 | # https://github.com/kamyu104/GoogleCodeJam-2021/blob/main/Round%201B/broken_clock.py
19 | def linear_congruence(a, m, b): # Time: O(logN), the same as gcd, Space: O(logN)
20 | # gcd(a, m) = g and g|b, find x, s.t. ax % m = b % m
21 | # => (a%m)x = my+(b%m)
22 | # => gcd(m, a%m) = g and g|-(b%m), find y, s.t. my % (a%m) = -(b%m) % (a%m)
23 | # => y = linear_congruence(m, a%m, -(b%m))
24 | # => x = (my+(b%m))/(a%m)
25 | ambs = []
26 | while m:
27 | a, m, b = m, a%m, -(b%m)
28 | if m:
29 | ambs.append((m, a, -b))
30 | x = a # a is gcd
31 | while ambs:
32 | a, m, b = ambs.pop()
33 | x = (m*x+b)//a
34 | return x
35 |
36 | def divisible_divisions():
37 | S, D = raw_input().strip().split()
38 | S, D = map(int, list(S)), int(D)
39 |
40 | cnts = Counter([1])
41 | d_remain = D
42 | for p in [2, 5]:
43 | while d_remain%p == 0:
44 | d_remain //= p
45 | cnts[p] += 1
46 | l = max(cnts.itervalues()) # l = O(logD)
47 |
48 | curr1, basis1 = 0, 1
49 | for i in reversed(xrange(len(S))):
50 | curr1 = (curr1 + S[i]*basis1) % d_remain
51 | basis1 = basis1*10 % d_remain
52 |
53 | # dp1[i]: count of divisible divisions of this prefix whose last division is divisible by D ends at S[i-1]
54 | # dp2[i]: count of divisible divisions of this prefix whose last division is not divisible by D ends at S[i-1]
55 | w = l+1
56 | dp1, dp2, suffix = [[0]*w for _ in xrange(3)]
57 | dp1[0], suffix[0] = 1, curr1
58 | prefix_total, prefix_dp1 = [Counter() for _ in xrange(2)]
59 | accu_dp1, d_2_5 = 1, D//d_remain
60 | inv_10_mod_d_remain = linear_congruence(10, d_remain, 1)%d_remain # Time: O(logD)
61 | for i in xrange(1, len(S)+1):
62 | basis1 = basis1*inv_10_mod_d_remain % d_remain
63 | curr1 = (curr1 - S[i-1]*basis1) % d_remain
64 | dp1[i%w], dp2[i%w], suffix[i%w] = 0, accu_dp1, curr1
65 | curr2, basis2 = 0, 1
66 | for k in xrange(1, l+1): # O(logD) times
67 | if i-k < 0:
68 | break
69 | j = i-k
70 | curr2 = (curr2 + S[j]*basis2) % d_2_5
71 | if k == l:
72 | prefix_total[suffix[j%w]] = addmod(prefix_total[suffix[j%w]], addmod(dp1[j%w], dp2[j%w]))
73 | prefix_dp1[suffix[j%w]] = addmod(prefix_dp1[suffix[j%w]], dp1[j%w])
74 | if curr2 == 0:
75 | # since all(S[j:i]%d_2_5 == 0 for j in xrange(i-l+1)) is true,
76 | # find sum(cnt[j] for j in xrange(i-l+1) if suffix[j] == suffix[i]) <=> find sum(cnt[j] for j in xrange(i-l+1) if S[j:i]%D == 0)
77 | dp1[i%w] = addmod(dp1[i%w], prefix_total[suffix[i%w]]) # prefix_total[suffix[i]] = sum(dp1[j]+dp2[j] for j in xrange(i-l+1) if suffix[j] == suffix[i])%MOD
78 | dp2[i%w] = addmod(dp2[i%w], -prefix_dp1[suffix[i%w]]) # prefix_dp1[suffix[i]] = sum(dp1[j] for j in xrange(i-l+1) if suffix[j] == suffix[i])%MOD
79 | break
80 | if curr2 == 0 and suffix[j%w] == suffix[i%w]: # (S[j:i]%d_2_5 == 0) and (suffix[j]-suffix[i] == 0 <=> (S[j:i]*10^(len(S)-i)%d_remain == 0 and gcd(d_remain, 10^(len(S)-i)) == 1) <=> S[j:i]%d_remain == 0) <=> S[j:i]%D == 0
81 | dp1[i%w] = addmod(dp1[i%w], addmod(dp1[j%w], dp2[j%w]))
82 | dp2[i%w] = addmod(dp2[i%w], -dp1[j%w])
83 | basis2 = basis2*10 % d_2_5
84 | accu_dp1 = addmod(accu_dp1, dp1[i%w])
85 | return addmod(dp1[len(S)%w], dp2[len(S)%w])
86 |
87 | MOD = 10**9+7
88 | for case in xrange(input()):
89 | print 'Case #%d: %s' % (case+1, divisible_divisions())
90 |
--------------------------------------------------------------------------------
/Round 3/fence_design.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 3 - Problem C. Fence Design
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000436142/0000000000813bc7
5 | #
6 | # Time: O(NlogN) on average, pass in PyPy2 but Python2
7 | # Space: O(N)
8 | #
9 |
10 | from random import seed, randint
11 |
12 | # Compute the cross product of vectors AB and AC
13 | CW, COLLINEAR, CCW = range(-1, 2)
14 | def ccw(A, B, C):
15 | area = (B[0]-A[0])*(C[1]-A[1]) - (B[1]-A[1])*(C[0]-A[0])
16 | return CCW if area > 0 else CW if area < 0 else COLLINEAR
17 |
18 | def same_side(A, B, C, D):
19 | return ccw(A,C,D) == 0 or ccw(B,C,D) == 0 or ccw(A,C,D) == ccw(B,C,D)
20 |
21 | def rotate(hull, split):
22 | for i in xrange(len(hull)):
23 | if hull[i] in split and hull[(i-1)%len(hull)] in split:
24 | return hull[i:]+hull[:i]
25 | return hull[:]
26 |
27 | def add_result(result, x):
28 | result.add(tuple(sorted(x)))
29 |
30 | def add_triangle(P, left_ccw, right_cw, result, lookup):
31 | p, q = 0, 1
32 | while True:
33 | p1 = (p+1)%len(left_ccw)
34 | if ccw(P[left_ccw[p1]], P[left_ccw[p]], P[right_cw[q]]) == CCW:
35 | add_result(result, [left_ccw[p1], right_cw[q]])
36 | lookup.add(left_ccw[p]) # inside the convex hull
37 | p = p1
38 | continue
39 | q1 = (q+1)%len(right_cw)
40 | if ccw(P[left_ccw[p]], P[right_cw[q]], P[right_cw[q1]]) == CCW:
41 | add_result(result, [right_cw[q1], left_ccw[p]])
42 | lookup.add(right_cw[q]) # inside the convex hull
43 | q = q1
44 | continue
45 | break
46 |
47 | def conquer(P, left, right, split, result): # Time: O(N)
48 | if len(left) == 2:
49 | return right
50 | if len(right) == 2:
51 | return left
52 | lookup = set()
53 | left_ccw, right_cw = rotate(left, split), rotate(right[::-1], split)
54 | add_triangle(P, left_ccw, right_cw, result, lookup)
55 | right_ccw, left_cw = rotate(right, split), rotate(left[::-1], split)
56 | add_triangle(P, right_ccw, left_cw, result, lookup)
57 | return [x for x in left_ccw if x not in lookup] + \
58 | [x for x in right_ccw[1:-1] if x not in lookup]
59 |
60 | def divide(P, f, curr, split, result): # depth at most O(logN) on average => Time: O(NlogN)
61 | if len(curr) == 2:
62 | return curr
63 | if len(curr) == 3: # terminal case
64 | p = next(p for p in curr if p not in split)
65 | for x in split:
66 | add_result(result, [p, x])
67 | return [p, split[0], split[1]] if ccw(P[p], P[split[0]], P[split[1]]) == CCW else [p, split[1], split[0]]
68 | if f: # prefer to use pre-placed fence
69 | new_split = f.pop()
70 | else:
71 | while True:
72 | idx = randint(0, len(curr)-1)
73 | p = curr[idx]
74 | curr[idx], curr[-1] = curr[-1], curr[idx]
75 | q = curr[randint(0, len(curr)-2)]
76 | if p > q:
77 | p, q = q, p
78 | if (p, q) not in result:
79 | break
80 | new_split = (p, q)
81 | add_result(result, new_split)
82 | left = [x for x in curr if ccw(P[new_split[0]], P[new_split[1]], P[x]) != CCW]
83 | right = [x for x in curr if ccw(P[new_split[0]], P[new_split[1]], P[x]) != CW]
84 | return conquer(P,
85 | divide(P, f if f and f[-1][0] in left and f[-1][1] in left else [], left, new_split, result),
86 | divide(P, f if f and f[-1][0] in right and f[-1][1] in right else [], right, new_split, result),
87 | new_split, result)
88 |
89 | def fence_design():
90 | N = input()
91 | P = [map(int, raw_input().strip().split()) for _ in xrange(N)]
92 | f = [map(lambda x: int(x)-1, raw_input().strip().split()) for _ in xrange(2)]
93 | f = [tuple(sorted(x)) for x in f]
94 |
95 | if not same_side(P[f[0][0]], P[f[0][1]], P[f[1][0]], P[f[1][1]]):
96 | # make sure f[0] will be on the same side of f[1]
97 | f[0], f[1] = f[1], f[0]
98 | result = set()
99 | hull = divide(P, f[:], range(len(P)), [], result)
100 | assert(len(result) == 3*N-3-len(hull))
101 | return "%s\n"%(len(result)-2)+"\n".join("%s %s"%(x[0]+1, x[1]+1) for x in [x for x in result if x not in f])
102 |
103 | seed(0)
104 | for case in xrange(input()):
105 | print 'Case #%d: %s' % (case+1, fence_design())
106 |
--------------------------------------------------------------------------------
/Round 3/binary_search_game.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 3 - Problem D. Binary Search Game
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000436142/0000000000813e1b
5 | #
6 | # Time: O(N^2 + N * 2^(2^(L-1)) * 2^L), pass in PyPy2 but Python2
7 | # Space: O(N^2 + 2^L)
8 | #
9 |
10 | from collections import defaultdict, Counter
11 |
12 | def addmod(a, b):
13 | return (a+b)%MOD
14 |
15 | def mulmod(a, b):
16 | return (a*b)%MOD
17 |
18 | def inverse(n): # compute and cache, at most O(N) time and O(N) space in all test cases
19 | while len(inv) <= n: # lazy initialization
20 | inv.append(inv[MOD%len(inv)]*(MOD-MOD//len(inv)) % MOD) # https://cp-algorithms.com/algebra/module-inverse.html
21 | return inv[n]
22 |
23 | def inverse_factorial(n): # compute and cache, at most O(N) time and O(N) space in all test cases
24 | while len(inv_fact) <= n: # lazy initialization
25 | inv_fact.append(inv_fact[-1]*inverse(len(inv_fact)) % MOD)
26 | return inv_fact[n]
27 |
28 | def power(x, y): # compute and cache, at most O(N^2) time and O(N^2) space in each test case
29 | while len(POW[0][x]) <= y: # lazy initialization
30 | POW[0][x].append(mulmod(POW[0][x][-1], x))
31 | return POW[0][x][y]
32 |
33 | # f(x) = c(N+1)*x^(N+1) + c(N)*x^N + ... + c(0)*x^0
34 | # given f(0), f(1), ... f(N+1), compute f(M)
35 | # usually, this should be done in O(N^2) time,
36 | # since x0, x1, ..., x(n-1) are consecutive integers,
37 | # we can compute in O(N) time
38 | def lagrange_interpolation(f, x): # Time: O(N)
39 | n = len(f)
40 | prefix = [1]*n
41 | for i in xrange(1, len(f)): # (x-x0)*(x-x1)* ... * (x-x(n-1))
42 | prefix[i] = mulmod(prefix[i-1], x-(i-1))
43 | suffix = [1]*n
44 | for i in reversed(xrange(len(f)-1)):
45 | suffix[i] = mulmod(suffix[i+1], x-(i+1))
46 | result = 0
47 | for i in xrange(len(f)):
48 | a = mulmod(prefix[i], suffix[i]) # (x-x0)*(x-x1)* ... * (x-x(n-1)) / (x-xi)
49 | b = mulmod(inverse_factorial(i), mulmod((-1)**((n-1-i)%2), inverse_factorial(n-1-i))) # (1/i!) * ((-1)^((n-1-i)%2)/(n-1-i)!)
50 | result = addmod(result, mulmod(f[i], mulmod(a, b)))
51 | return result
52 |
53 | def mask_to_set(R, mask): # Time: O(N)
54 | result = set()
55 | for i in R:
56 | if mask&1:
57 | result.add(i)
58 | mask >>= 1
59 | return result
60 |
61 | # given chosen subset C from R where card values are all >= k,
62 | # count the number of ways to get final score >= k by considering the card values of U
63 | def count(N, M, L, A, U, C, k): # Time: O(2^L), Space: O(2^L)
64 | g = max(min(M-(k-1), M), 0) # number of choices greater or equal to k
65 | l = max(min(k-1, M), 0) # number of choices less than k
66 | # last decision done by whom would affect initial dp
67 | dp = [[l, g] if i in U else [0, 1] if i in C else [1, 0] for i in A] if L%2 else \
68 | [[g, l] if i in U else [1, 0] if i in C else [0, 1] for i in A]
69 | while len(dp) != 1:
70 | dp = [[addmod(addmod(mulmod(dp[2*i][1], dp[2*i+1][1]),
71 | mulmod(dp[2*i][1], dp[2*i+1][0])),
72 | mulmod(dp[2*i][0], dp[2*i+1][1])),
73 | mulmod(dp[2*i][0], dp[2*i+1][0])]
74 | for i in xrange(len(dp)//2)]
75 | return mulmod(mulmod(dp[0][0],
76 | power(g, len(C))),
77 | power(l, N-len(U)-len(C)))
78 |
79 | def binary_search_game():
80 | N, M, L = map(int, raw_input().strip().split())
81 | A = map(lambda x: int(x)-1, raw_input().strip().split())
82 |
83 | POW[0] = defaultdict(lambda:[1]) # cleanup global used cache to save space
84 | cnt = Counter(A)
85 | U, R = set(), set() # unique set and repeated set
86 | for i in A:
87 | if cnt[i] == 1:
88 | U.add(i)
89 | else:
90 | R.add(i)
91 | Z = set(i for i in xrange(N) if i not in U and i not in R) # unused set
92 | N -= len(Z)
93 | assert(len(R) <= len(A)//2)
94 | # f(x) which means the number of ways where the final scores is >= x, is a polynomial of x with at most N-degree
95 | # accumulated f(x) which means the sum of final scores with x^N different games, is a polynomial of x with at most (N+1)-degree by Faulhaber's formula,
96 | # and could be determinated by N+2 values of accumulated f(x)
97 | f = [0]*(min(N+1, M)+1) # if M < N+1, we can also just loop until f[M] is computed
98 | for mask in xrange(2**len(R)): # O(2^(2^(L-1))) times
99 | C = mask_to_set(R, mask)
100 | for k in xrange(1, len(f)): # O(N) times
101 | f[k] = addmod(f[k], count(N, M, L, A, U, C, k)) # Time: O(2^L)
102 | for k in xrange(1, len(f)):
103 | f[k] = addmod(f[k], f[k-1]) # accumulate f
104 | return mulmod(lagrange_interpolation(f, M), power(M, len(Z))) # Time: O(N)
105 |
106 | MOD = 10**9+7
107 | inv = [0, 1]
108 | inv_fact = [1, 1]
109 | POW = [None]
110 | for case in xrange(input()):
111 | print 'Case #%d: %s' % (case+1, binary_search_game())
112 |
--------------------------------------------------------------------------------
/Round 3/binary_search_game2.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 3 - Problem D. Binary Search Game
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000436142/0000000000813e1b
5 | #
6 | # Time: O(N * 2^(2^(L-1)) * 2^L), pass in PyPy2 but Python2
7 | # Space: O(N + L)
8 | #
9 | # optimized from binary_search_game.py, less time and space complexity, faster in power function,
10 | # but slower in recursive check function (iterative one is even slower since 2^L is small) compared to dp,
11 | # so overall runtime is slower
12 | #
13 |
14 | from collections import Counter
15 |
16 | def addmod(a, b):
17 | return (a+b)%MOD
18 |
19 | def mulmod(a, b):
20 | return (a*b)%MOD
21 |
22 | def inverse(n): # compute and cache, at most O(N) time and O(N) space in all test cases
23 | while len(inv) <= n: # lazy initialization
24 | inv.append(inv[MOD%len(inv)]*(MOD-MOD//len(inv)) % MOD) # https://cp-algorithms.com/algebra/module-inverse.html
25 | return inv[n]
26 |
27 | def inverse_factorial(n): # compute and cache, at most O(N) time and O(N) space in all test cases
28 | while len(inv_fact) <= n: # lazy initialization
29 | inv_fact.append(inv_fact[-1]*inverse(len(inv_fact)) % MOD)
30 | return inv_fact[n]
31 |
32 | # f(x) = c(N+1)*x^(N+1) + c(N)*x^N + ... + c(0)*x^0
33 | # given f(0), f(1), ... f(N+1), compute f(M)
34 | # usually, this should be done in O(N^2) time,
35 | # since x0, x1, ..., x(n-1) are consecutive integers,
36 | # we can compute in O(N) time
37 | def lagrange_interpolation(f, x): # Time: O(N)
38 | n = len(f)
39 | prefix = [1]*n
40 | for i in xrange(1, len(f)): # (x-x0)*(x-x1)* ... * (x-x(n-1))
41 | prefix[i] = mulmod(prefix[i-1], x-(i-1))
42 | suffix = [1]*n
43 | for i in reversed(xrange(len(f)-1)):
44 | suffix[i] = mulmod(suffix[i+1], x-(i+1))
45 | result = 0
46 | for i in xrange(len(f)):
47 | a = mulmod(prefix[i], suffix[i]) # (x-x0)*(x-x1)* ... * (x-x(n-1)) / (x-xi)
48 | b = mulmod(inverse_factorial(i), mulmod((-1)**((n-1-i)%2), inverse_factorial(n-1-i))) # (1/i!) * ((-1)^((n-1-i)%2)/(n-1-i)!)
49 | result = addmod(result, mulmod(f[i], mulmod(a, b)))
50 | return result
51 |
52 | def mask_to_set(R, mask): # Time: O(N)
53 | result = set()
54 | for i in R:
55 | if mask&1:
56 | result.add(i)
57 | mask >>= 1
58 | return result
59 |
60 | def check(A, values, left, right): # Time: O(2^L), Space: O(L)
61 | if left == right:
62 | return values[A[left]]
63 | mid = left + (right-left)//2
64 | win1, lose1 = check(A, values, left, mid)
65 | win2, lose2 = check(A, values, mid+1, right)
66 | return [addmod(addmod(mulmod(lose1, lose2),
67 | mulmod(lose1, win2)),
68 | mulmod(win1, lose2)),
69 | mulmod(win1, win2)]
70 |
71 | # given chosen subset C from R where card values are all >= k,
72 | # count the number of ways to get final score >= k by considering the card values of U
73 | def count(N, M, L, A, U, R, C, k): # Time: O(2^L), Space: O(N + L)
74 | g = max(min(M-(k-1), M), 0) # number of choices greater or equal to k
75 | l = max(min(k-1, M), 0) # number of choices less than k
76 | # last decision done by whom would affect initial dp
77 | values = {i:[l, g] if i in U else [0, 1] if i in C else [1, 0] for i in set.union(U, R)} if L%2 else \
78 | {i:[g, l] if i in U else [1, 0] if i in C else [0, 1] for i in set.union(U, R)}
79 | return mulmod(mulmod(check(A, values, 0, len(A)-1)[0],
80 | pow(g, len(C), MOD)),
81 | pow(l, N-len(U)-len(C), MOD)) # since pow(x, N, MOD) is O(logN), and N <= 32, we treat it as O(1)
82 |
83 | def binary_search_game():
84 | N, M, L = map(int, raw_input().strip().split())
85 | A = map(lambda x: int(x)-1, raw_input().strip().split())
86 |
87 | cnt = Counter(A)
88 | U, R = set(), set() # unique set and repeated set
89 | for i in A:
90 | if cnt[i] == 1:
91 | U.add(i)
92 | else:
93 | R.add(i)
94 | Z = set(i for i in xrange(N) if i not in U and i not in R) # unused set
95 | N -= len(Z)
96 | assert(len(R) <= len(A)//2)
97 | # f(x) which means the number of ways where the final scores is >= x, is a polynomial of x with at most N-degree
98 | # accumulated f(x) which means the sum of final scores with x^N different games, is a polynomial of x with at most (N+1)-degree by Faulhaber's formula,
99 | # and could be determinated by N+2 values of accumulated f(x)
100 | f = [0]*(min(N+1, M)+1) # if M < N+1, we can also just loop until f[M] is computed
101 | for mask in xrange(2**len(R)): # O(2^(2^(L-1))) times
102 | C = mask_to_set(R, mask)
103 | for k in xrange(1, len(f)): # O(N) times
104 | f[k] = addmod(f[k], count(N, M, L, A, U, R, C, k)) # Time: O(2^L)
105 | for k in xrange(1, len(f)):
106 | f[k] = addmod(f[k], f[k-1]) # accumulate f
107 | return mulmod(lagrange_interpolation(f, M), pow(M, len(Z), MOD)) # Time: O(N), since pow(x, N, MOD) is O(logN), and N <= 32, we treat it as O(1)
108 |
109 | MOD = 10**9+7
110 | inv = [0, 1]
111 | inv_fact = [1, 1]
112 | for case in xrange(input()):
113 | print 'Case #%d: %s' % (case+1, binary_search_game())
114 |
--------------------------------------------------------------------------------
/Round 3/binary_search_game3.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Round 3 - Problem D. Binary Search Game
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000436142/0000000000813e1b
5 | #
6 | # Time: O(2^(2^(L-1)) * (2^L + N^2) + N^3)
7 | # Space: O(N^2 + L)
8 | #
9 |
10 | from collections import Counter
11 |
12 | def addmod(a, b):
13 | return (a+b)%MOD
14 |
15 | def submod(a, b):
16 | return (a-b)%MOD
17 |
18 | def mulmod(a, b):
19 | return (a*b)%MOD
20 |
21 | def inverse(n): # compute and cache, at most O(N) time and O(N) space in all test cases
22 | while len(inv) <= n: # lazy initialization
23 | inv.append(inv[MOD%len(inv)]*(MOD-MOD//len(inv)) % MOD) # https://cp-algorithms.com/algebra/module-inverse.html
24 | return inv[n]
25 |
26 | def inverse_factorial(n): # compute and cache, at most O(N) time and O(N) space in all test cases
27 | while len(inv_fact) <= n: # lazy initialization
28 | inv_fact.append(inv_fact[-1]*inverse(len(inv_fact)) % MOD)
29 | return inv_fact[n]
30 |
31 | # f(x) = c(N+1)*x^(N+1) + c(N)*x^N + ... + c(0)*x^0
32 | # given f(0), f(1), ... f(N+1), compute f(M)
33 | # usually, this should be done in O(N^2) time,
34 | # since x0, x1, ..., x(n-1) are consecutive integers,
35 | # we can compute in O(N) time
36 | def lagrange_interpolation(f, x): # Time: O(N)
37 | n = len(f)
38 | prefix = [1]*n
39 | for i in xrange(1, len(f)): # (x-x0)*(x-x1)* ... * (x-x(n-1))
40 | prefix[i] = mulmod(prefix[i-1], x-(i-1))
41 | suffix = [1]*n
42 | for i in reversed(xrange(len(f)-1)):
43 | suffix[i] = mulmod(suffix[i+1], x-(i+1))
44 | result = 0
45 | for i in xrange(len(f)):
46 | a = mulmod(prefix[i], suffix[i]) # (x-x0)*(x-x1)* ... * (x-x(n-1)) / (x-xi)
47 | b = mulmod(inverse_factorial(i), mulmod((-1)**((n-1-i)%2), inverse_factorial(n-1-i))) # (1/i!) * ((-1)^((n-1-i)%2)/(n-1-i)!)
48 | result = addmod(result, mulmod(f[i], mulmod(a, b)))
49 | return result
50 |
51 | def mask_to_set(R, mask): # Time: O(N)
52 | result = set()
53 | for i in R:
54 | if mask&1:
55 | result.add(i)
56 | mask >>= 1
57 | return result
58 |
59 | def check(A, values, left, right): # Time: O(2^L), Space: O(L)
60 | if left == right:
61 | return values[A[left]]
62 | mid = left + (right-left)//2
63 | win1, lose1 = check(A, values, left, mid)
64 | win2, lose2 = check(A, values, mid+1, right)
65 | return [addmod(addmod(mulmod(lose1, lose2),
66 | mulmod(lose1, win2)),
67 | mulmod(win1, lose2)),
68 | mulmod(win1, win2)]
69 |
70 | # given f(x) = (M-(x-1))^a*(x-1)^b*M^(N-a-b), f(0) = 0
71 | # compute accumulated f(M) by Lagrange interpolation
72 | def g(N, M, a, b, lookup): # compute and cache, at most O(N^3) time and O(N^2) space in each test case
73 | if (a, b) not in lookup: # lazy initialization
74 | f = [mulmod(mulmod(pow(M-(k-1), a, MOD), pow(k-1, b, MOD)), pow(M, N-a-b, MOD)) if k else 0 for k in xrange(min(a+b+1, M)+1)]
75 | for k in xrange(1, len(f)):
76 | f[k] = addmod(f[k], f[k-1]) # accumulate f
77 | lookup[(a, b)] = lagrange_interpolation(f, M)
78 | return lookup[(a, b)]
79 |
80 | def binary_search_game():
81 | N, M, L = map(int, raw_input().strip().split())
82 | A = map(lambda x: int(x)-1, raw_input().strip().split())
83 |
84 | left = {A[i] for i in xrange(len(A)//2)} # left half of board set
85 | right = {A[i] for i in xrange(len(A)//2, len(A))} # right half of board set
86 | both = left.intersection(right)
87 | left = {i for i in left if i not in both}
88 | right = {i for i in right if i not in both}
89 | default_state = [1, 0] if L%2 else [0, 1]
90 | chosen_state = default_state[::-1]
91 | result, values, lookup = 0, [default_state for _ in xrange(N)], {}
92 | for mask_both in xrange(2**len(both)):
93 | left_cnt, right_cnt = Counter(), Counter()
94 | both_C = mask_to_set(both, mask_both)
95 | for i in both:
96 | values[i] = chosen_state if i in both_C else default_state
97 | for mask_left in xrange(2**len(left)): # mask_left and mask_right are independent of states, loop O(2^(len(both)+len(left))) <= O(2^(2^L/2)) times
98 | left_C = mask_to_set(left, mask_left)
99 | for i in left:
100 | values[i] = chosen_state if i in left_C else default_state
101 | left_cnt[len(left_C)] += check(A, values, 0, (len(A)-1)//2)[0] # count if alice loses, Time: O(2^L)
102 | for mask_right in xrange(2**len(right)): # mask_left and mask_right are independent of states, loop O(2^(len(both)+len(right))) <= O(2^(2^L/2)) times
103 | right_C = mask_to_set(right, mask_right)
104 | for i in right:
105 | values[i] = chosen_state if i in right_C else default_state
106 | right_cnt[len(right_C)] += check(A, values, (len(A)-1)//2+1, len(A)-1)[0] # count if alice loses, Time: O(2^L)
107 | result = addmod(result, g(N, M, len(both_C), len(both)-len(both_C), lookup)) # add all count of both_C
108 | for i in xrange(len(left)+1): # O(N^2) times
109 | for j in xrange(len(right)+1):
110 | cnt = g(N, M, len(both_C)+i+j, (len(both)-len(both_C))+(len(left)-i)+(len(right)-j), lookup)
111 | result = submod(result, mulmod(mulmod(left_cnt[i], right_cnt[j]), cnt)) # sub lose count of both_C
112 | return result
113 |
114 | MOD = 10**9+7
115 | inv = [0, 1]
116 | inv_fact = [1, 1]
117 | for case in xrange(input()):
118 | print 'Case #%d: %s' % (case+1, binary_search_game())
119 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [GoogleCodeJam 2021](https://codingcompetitions.withgoogle.com/codejam/archive/2021)  [](./LICENSE)  
2 |
3 | * Python solutions of Google Code Jam 2021. Solution begins with `*` means it will get TLE in the largest data set.
4 | * Total computation amount > `10^8` is not friendly for Python to solve in 5 ~ 15 seconds.
5 | * A problem was marked as `Very Hard` means that it was an unsolved one during the contest and may not be that difficult.
6 | * From 2021-04, `PyPy3` is supported by the online judge.
7 | * From 2021-11, `Python2/PyPy2` is no longer supported by the online judge.
8 | * You need to run `2to3 -n -W --add-suffix=3 solution.py` to convert the solution into `Python3/PyPy3`.
9 |
10 | ## Rounds
11 |
12 | * [Code Jam 2020](https://github.com/kamyu104/GoogleCodeJam-2020)
13 | * [Qualification Round](https://github.com/kamyu104/GoogleCodeJam-2021#qualification-round)
14 | * [Round 1A](https://github.com/kamyu104/GoogleCodeJam-2021#round-1a)
15 | * [Round 1B](https://github.com/kamyu104/GoogleCodeJam-2021#round-1b)
16 | * [Round 1C](https://github.com/kamyu104/GoogleCodeJam-2021#round-1c)
17 | * [Round 2](https://github.com/kamyu104/GoogleCodeJam-2021#round-2)
18 | * [Round 3](https://github.com/kamyu104/GoogleCodeJam-2021#round-3)
19 | * [Virtual World Finals](https://github.com/kamyu104/GoogleCodeJam-2021#virtual-world-finals)
20 | * [Code Jam 2022](https://github.com/kamyu104/GoogleCodeJam-2022)
21 |
22 | ## Qualification Round
23 | | # | Title | Solution | Time | Space | Difficulty | Tag | Note |
24 | |---| ----- | -------- | ---- | ----- | ---------- | --- | ---- |
25 | |A| [Reversort](https://codingcompetitions.withgoogle.com/codejam/round/000000000043580a/00000000006d0a5c)| [Python](./Qualification%20Round/reversort.py)| _O(N^2)_ | _O(1)_ | Easy | | Simulation |
26 | |B| [Moons and Umbrellas](https://codingcompetitions.withgoogle.com/codejam/round/000000000043580a/00000000006d1145)| [Python](./Qualification%20Round/moons_and_umbrellas.py)| _O(N)_ | _O(1)_ | Easy | | DP |
27 | |C| [Reversort Engineering](https://codingcompetitions.withgoogle.com/codejam/round/000000000043580a/00000000006d12d7)| [Python](./Qualification%20Round/reversort_engineering.py) [Python](./Qualification%20Round/reversort_engineering2.py)| _O(N)_ | _O(1)_ | Easy | | Greedy |
28 | |D| [Median Sort](https://codingcompetitions.withgoogle.com/codejam/round/000000000043580a/00000000006d1284)| [Python](./Qualification%20Round/median_sort.py) | _O(N^2)_ | _O(1)_ | Medium | | Ternary Search, Insertion Sort |
29 | |E| [Cheating Detection](https://codingcompetitions.withgoogle.com/codejam/round/000000000043580a/00000000006d1155)| [Python](./Qualification%20Round/cheating_detection.py) [Python](./Qualification%20Round/cheating_detection2.py) [Python](./Qualification%20Round/cheating_detection3.py) [Python](./Qualification%20Round/cheating_detection4.py) | _O(S * Q)_ | _O(S + Q)_ | Hard | | Uniform Distribution, Inversions Counting, Correlation Coefficient |
30 |
31 | ## Round 1A
32 | | # | Title | Solution | Time | Space | Difficulty | Tag | Note |
33 | |---| ----- | -------- | ---- | ----- | ---------- | --- | ---- |
34 | |A| [Append Sort](https://codingcompetitions.withgoogle.com/codejam/round/000000000043585d/00000000007549e5)| [Python](./Round%201A/append_sort.py) [Python](./Round%201A/append_sort2.py) | _O(N * log(MAX_X))_ | _O(1)_ | Easy | | Greedy |
35 | |B| [Prime Time](https://codingcompetitions.withgoogle.com/codejam/round/000000000043585d/00000000007543d8)| [Python](./Round%201A/prime_time.py)| _O((MAX_P * logX) * (M + logX))_ | _O(1)_ | Medium | | Math, Prime Factorization, Pruning |
36 | |C| [Hacked Exam](https://codingcompetitions.withgoogle.com/codejam/round/000000000043585d/0000000000754750)| [Python](./Round%201A/hacked_exam.py) | precompute: _O(MAX_Q^2)_
runtime: _O(Q)_ | _O(MAX_Q^2)_ | Hard | | Binomial Coefficients, DP, Math, Expected Value |
37 |
38 | ## Round 1B
39 | | # | Title | Solution | Time | Space | Difficulty | Tag | Note |
40 | |---| ----- | -------- | ---- | ----- | ---------- | --- | ---- |
41 | |A| [Broken Clock](https://codingcompetitions.withgoogle.com/codejam/round/0000000000435baf/00000000007ae694)| [Python](./Round%201B/broken_clock.py) | _O(1)_ | _O(1)_ | Medium | | Math, Linear Congruence |
42 | |B| [Subtransmutation](https://codingcompetitions.withgoogle.com/codejam/round/0000000000435baf/00000000007ae4aa)| [Python](./Round%201B/subtransmutation.py) [Python](./Round%201B/subtransmutation2.py) | _O(MAX_M^2)_ | _O(MAX_M)_ | Medium | | Math, Bézout's Identity, Greedy |
43 | |C| [Digit Blocks](https://codingcompetitions.withgoogle.com/codejam/round/0000000000435baf/00000000007ae37b)| [Python](./Round%201B/digit_blocks.py) | precompute: _O(N^3 * B * D)_
runtime: _O(N * B)_ | _O(N^3 * B * D)_ | Hard | | DP, Math, Expected Value |
44 |
45 | ## Round 1C
46 | | # | Title | Solution | Time | Space | Difficulty | Tag | Note |
47 | |---| ----- | -------- | ---- | ----- | ---------- | --- | ---- |
48 | |A| [Closest Pick](https://codingcompetitions.withgoogle.com/codejam/round/00000000004362d7/00000000007c0f00)| [Python](./Round%201C/closest_pick.py) | _O(NlogN)_ | _O(N)_ | Easy | | Math, Sort |
49 | |B| [Roaring Years](https://codingcompetitions.withgoogle.com/codejam/round/00000000004362d7/00000000007c0f01)| [Python](./Round%201C/roaring_years.py) | _O(D^2 * logD)_ | _O(D)_ | Medium | | Math, Binary Search |
50 | |C| [Double or NOTing](https://codingcompetitions.withgoogle.com/codejam/round/00000000004362d7/00000000007c1139)| [Python](./Round%201C/double_or_noting.py) [Python](./Round%201C/double_or_noting2.py) [Python](./Round%201C/double_or_noting3.py) | _O(\|E\| + \|S\|)_ | _O(\|E\| + \|S\|)_ | Hard | | Math, Bit Manipulation, KMP Algorithm |
51 |
52 | ## Round 2
53 | | # | Title | Solution | Time | Space | Difficulty | Tag | Note |
54 | |---| ----- | -------- | ---- | ----- | ---------- | --- | ---- |
55 | |A| [Minimum Sort](https://codingcompetitions.withgoogle.com/codejam/round/0000000000435915/00000000007dc51c)| [Python](./Round%202/minimum_sort.py) | _O(ClogN)_ | _O(1)_ | Easy | | Selection Sort |
56 | |B| [Matrygons](https://codingcompetitions.withgoogle.com/codejam/round/0000000000435915/00000000007dbf06)| [Python](./Round%202/matrygons.py) | precompute: _O(NlogN)_
runtime: _O(1)_ | _O(N)_ | Medium | | Precompute, DP |
57 | |C| [Hidden Pancakes](https://codingcompetitions.withgoogle.com/codejam/round/0000000000435915/00000000007dc20c)| [Python](./Round%202/hidden_pancakes.py) [Python](./Round%202/hidden_pancakes2.py) | _O(N)_ | _O(N)_ | Medium | | Math, Binomial Coefficients, DP |
58 | |D| [Retiling](https://codingcompetitions.withgoogle.com/codejam/round/0000000000435915/00000000007dc2de)| [Python](./Round%202/retiling.py) | _O((R * C)^3)_ | _O((R * C)^2)_ | Hard | | Hungarian Weighted Bipartite Matching |
59 |
60 | ## Round 3
61 | | # | Title | Solution | Time | Space | Difficulty | Tag | Note |
62 | |---| ----- | -------- | ---- | ----- | ---------- | --- | ---- |
63 | |A| [Build-A-Pair](https://codingcompetitions.withgoogle.com/codejam/round/0000000000436142/0000000000813aa8)| [PyPy](./Round%203/build_a_pair.py) [PyPy](./Round%203/build_a_pair2.py) [Python](./Round%203/build_a_pair3.py) | _O(N)_ | _O(1)_ | Easy | | Enumeration, Greedy |
64 | |B| [Square Free](https://codingcompetitions.withgoogle.com/codejam/round/0000000000436142/0000000000813e1a)| [Python](./Round%203/square_free.py) | _O(R^2 * C^2)_ | _O(R + C)_ | Medium | | Max Flow, Greedy |
65 | |C| [Fence Design](https://codingcompetitions.withgoogle.com/codejam/round/0000000000436142/0000000000813bc7)| [PyPy](./Round%203/fence_design.py) | _O(NlogN)_ on average | _O(N)_ | Hard | ❤️ | Geometry, Convex Hull, Divide and Conquer, Random |
66 | |D| [Binary Search Game](https://codingcompetitions.withgoogle.com/codejam/round/0000000000436142/0000000000813e1b)| [PyPy](./Round%203/binary_search_game.py) [PyPy](./Round%203/binary_search_game2.py) [Python](./Round%203/binary_search_game3.py) | _O(2^(2^(L-1)) * (2^L + N^2) + N^3)_ | _O(N^2 + L)_ | Hard | ❤️ | Combinatorics, Counting, DP, Greedy, Polynomial, Faulhaber's Formula, Lagrange Interpolation |
67 |
68 | ## Virtual World Finals
69 | You can relive the magic of the 2021 Code Jam Virtual World Finals by watching the [Live Stream Recording](https://codingcompetitionsonair.withgoogle.com/events/cj-2021/watch?talk=cj-2021-livestream) of the competition, problem explanations, and announcement of winners.
70 |
71 | | # | Title | Solution | Time | Space | Difficulty | Tag | Note |
72 | |---| ----- | -------- | ---- | ----- | ---------- | --- | ---- |
73 | |A| [Cutting Cake](https://codingcompetitions.withgoogle.com/codejam/round/0000000000436329/000000000084fba1)| [Python](./Virtual%20World%20Finals/cutting_cake.py) | _O(NlogN)_ | _O(N)_ | Medium || Math, Line Sweep
74 | |B| [Slide Circuits](https://codingcompetitions.withgoogle.com/codejam/round/0000000000436329/000000000084f7b2)| [Python](./Virtual%20World%20Finals/slide_circuits.py) [Python](./Virtual%20World%20Finals/slide_circuits2.py) [Python](./Virtual%20World%20Finals/slide_circuits3.py) | _O(B + N + SlogS)_ | _O(SlogS)_ | Medium || Graph, Hash, Prefix Sum
75 | |C| [Ropes](https://codingcompetitions.withgoogle.com/codejam/round/0000000000436329/000000000084fad0)| [PyPy](./Virtual%20World%20Finals/ropes.py) | _O(N^3)_ | _O(N^2)_ | Hard | ❤️ | Greedy
76 | |D| [Divisible Divisions](https://codingcompetitions.withgoogle.com/codejam/round/0000000000436329/000000000084fb3a)| [Python](./Virtual%20World%20Finals/divisible_divisions.py) [Python](./Virtual%20World%20Finals/divisible_divisions2.py) | _O(\|S\|logD)_ | _O(min(\|S\|logD, D))_ | Medium || Math, Linear Congruence, Chinese Remainder Theorem, DP, Prefix Sum
77 | |E| [Infinitree](https://codingcompetitions.withgoogle.com/codejam/round/0000000000436329/000000000084fc01)| [PyPy](./Virtual%20World%20Finals/infinitree_concise.py) [PyPy3](./Virtual%20World%20Finals/infinitree_concise.py3) | _O(N^3.5 * logN + N^3 * logB)_ | _O(N^2.5 * logN + N^2 * logB)_ | Very Hard | ❤️ | Graph, Binary Tree, Matrix Exponentiation, Matrix Power Series, Prefix Sum, Binary Search, LCA, Tarjan's Algorithm
78 |
--------------------------------------------------------------------------------
/Virtual World Finals/infinitree_concise.py3:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Virtual World Finals - Problem E. Infinitree
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000436329/000000000084fc01
5 | #
6 | # Time: O(N^3.5 * logN + N^3 * logB), pass in PyPy3 but Python3
7 | # Space: O(N^2.5 * logN + N^2 * logB)
8 | #
9 | # concise solution of infinitree.py
10 | #
11 |
12 | # Template modified from:
13 | # https://github.com/kamyu104/GoogleCodeJam-2017/blob/master/Round%202/beaming_with_joy.py
14 | def strongly_connected_components(graph): # Time: O(|V| + |E|) = O(N + 2N) = O(N), Space: O(|V|) = O(N)
15 | def strongconnect(v, index_counter, index, lowlinks, stack, stack_set, result):
16 | index[v] = index_counter[0]
17 | lowlinks[v] = index_counter[0]
18 | index_counter[0] += 1
19 | stack_set.add(v)
20 | stack.append(v)
21 | for w in (graph[v] if v in graph else []):
22 | if w not in index:
23 | strongconnect(w, index_counter, index, lowlinks, stack, stack_set, result)
24 | lowlinks[v] = min(lowlinks[v], lowlinks[w])
25 | elif w in stack_set:
26 | lowlinks[v] = min(lowlinks[v], index[w])
27 | if lowlinks[v] == index[v]:
28 | connected_component = []
29 | w = None
30 | while w != v:
31 | w = stack.pop()
32 | stack_set.remove(w)
33 | connected_component.append(w)
34 | result.append(set(connected_component))
35 |
36 | index_counter, index, lowlinks = [0], {}, {}
37 | stack, stack_set = [], set()
38 | result = []
39 | strongconnect(ROOT_COLOR, index_counter, index, lowlinks, stack, stack_set, result) # modified, only care about reachable colors
40 | return result
41 |
42 | # return cycle_adj, cycle_length only if all reachable colors belong to at most one cycle
43 | def find_cycles(graph): # Time: O(N), Space: O(N)
44 | cycle_adj, cycle_length = {}, {}
45 | for scc in strongly_connected_components(graph):
46 | if any(sum(int(x in scc) for x in graph[node]) == 2 for node in scc): # this is optional optimization
47 | return {}, {} # have a reachable color belonging to more than one cycle, we only need to run single step solution
48 | if any(sum(int(x in scc) for x in graph[node]) != 1 for node in scc):
49 | continue
50 | node = next(iter(scc))
51 | for _ in range(len(scc)):
52 | cycle_adj[node] = next(x for x in graph[node] if x in scc)
53 | cycle_length[node] = len(scc)
54 | node = cycle_adj[node]
55 | return cycle_adj, cycle_length
56 |
57 | def floor_log2_x(x): # Time: O(logx)
58 | return x.bit_length()-1
59 |
60 | def identity_matrix(N): # Time: O(N)
61 | return [[int(i == j) for j in range(N)] for i in range(N)]
62 |
63 | def e(i, N): # Time: O(N)
64 | return [int(j == i) for j in range(N)]
65 |
66 | def matrix_mult(A, B, INF): # Time: O(N^2) if A is a 1 x N matrix and B is a N x N matrix, O(N^3) if A, B are both N x N matrixs
67 | result = [[0]*len(B[0]) for _ in range(len(A))]
68 | B_T = list(zip(*B))
69 | for result_i, A_i in zip(result, A):
70 | for j, B_T_i in enumerate(B_T):
71 | for A_i_j, B_T_i_j in zip(A_i, B_T_i):
72 | result_i[j] += A_i_j*B_T_i_j
73 | if result_i[j] > INF:
74 | result_i[j] = INF
75 | break
76 | return result
77 |
78 | def matrix_add(A, B, INF): # Time: O(N) if A, B are both 1 x N matrixs, O(N^2) if A, B are both N x N matrixs
79 | result = [[0]*len(B[0]) for _ in range(len(A))]
80 | for result_i, A_i, B_i in zip(result, A, B):
81 | for j in range(len(result_i)):
82 | result_i[j] = A_i[j]+B_i[j]
83 | if result_i[j] > INF:
84 | result_i[j] = INF
85 | return result
86 |
87 | # build [M, M^2, ..., M^(2^logx)] and [I, (I + M), (I + M + M^2 + M^3), (I + M + ... + M^(2^logx-1))]
88 | def build_powers_and_power_series(N, M, INF, x): # Time: O(N^3 * logx)
89 | logx = floor_log2_x(x)
90 | I = identity_matrix(N)
91 | # M_powers[i] for i in range(1+logx):
92 | # 0: M
93 | # 1: M^2
94 | # ...
95 | # logx: M^(2^logx)
96 | M_powers = [M]
97 | for _ in range(logx): # Time: O(N^3 * logx)
98 | M_powers.append(matrix_mult(M_powers[-1], M_powers[-1], INF))
99 | # M_power_series[i] for i in range(1+logx):
100 | # 0: I
101 | # 1: (I + M) * I = I + M
102 | # 2: (I + M^2) * (I + M) = I + M + M^2 + M^3
103 | # ...
104 | # logx: (I + M^(2^(logx-1))) * (I + M + ... + M^(2^(logx-1)-1)) = I + M + ... + M^(2^logx-1)
105 | M_power_series = [I]
106 | for i in range(logx): # Time: O(N^3 * logx)
107 | matrix = matrix_add(I, M_powers[i], INF)
108 | M_power_series.append(matrix_mult(matrix, M_power_series[-1], INF))
109 | return M_powers, M_power_series
110 |
111 | # V * M^x by vector-matrix or matrix-matrix exponentiation
112 | def get_V_M_power_x(M_powers, INF, V, x): # Time: O(N^2 * logx) if V is a 1 x N matrix, O(N^3 * logx) if V is a N x N matrix
113 | basis, i = 1, 0
114 | while basis <= x:
115 | if x&basis:
116 | V = matrix_mult(V, M_powers[i], INF)
117 | basis, i = basis<<1, i+1
118 | return V
119 |
120 | # v * (I + M + M^2 + ... + M^x) by vector-matrix exponentiation
121 | def get_v_M_power_series_x(N, M_powers, M_power_series, INF, v, x): # Time: O(N^2 * logx)
122 | x += 1
123 | u = [0]*N
124 | basis, i = 1, 0
125 | while basis <= x:
126 | if x&basis:
127 | # new_Pr = P_r*M^(2^i) + P_(2^i)
128 | # new_u = v * new_Pr = v * (P_r*M^(2^i) + P_(2^i)) = u*M^(2^i) + v*P_(2^i)
129 | u = matrix_add(matrix_mult([u], M_powers[i], INF),
130 | matrix_mult([v], M_power_series[i], INF),
131 | INF)[0]
132 | basis, i = basis<<1, i+1
133 | return u
134 |
135 | def get_depth(N, M_powers, M_power_series, INF, v, x): # Time: O(N^2 * logx)
136 | logx = floor_log2_x(x)
137 | result = 0
138 | u = [0]*N
139 | basis = 1<>= 1
151 | return result
152 |
153 | def get_single_step_position(M_powers, INF, ec, h, x): # Time: O(N^2 * logx)
154 | left_cnt = sum(get_V_M_power_x(M_powers, INF, [ec], h-1)[0])
155 | return (LEFT, x) if x < left_cnt else (RIGHT, x-left_cnt)
156 |
157 | def get_multiple_steps_position(M_powers, Mh_power_series, INF, logp, v, delta_h, ec, x): # Time: O(N^2 * log(delta_h))
158 | left_cnt = sum(get_V_M_power_x(M_powers, INF, matrix_mult([v], Mh_power_series[logp], INF), delta_h)[0])
159 | mid_cnt = sum(get_V_M_power_x(M_powers, INF, [ec], delta_h)[0])
160 | return 0 <= x-left_cnt < mid_cnt, x-left_cnt
161 |
162 | def infinitree():
163 | N, A, B = list(map(int, input().strip().split()))
164 | L = list(map(int, input().strip().split()))
165 | R = list(map(int, input().strip().split()))
166 |
167 | N += 1
168 | if A > B:
169 | A, B, = B, A
170 | INF = B
171 | M = [[0]*N for _ in range(N)]
172 | graph = {LEAF_COLOR:[]}
173 | for i in range(ROOT_COLOR, N):
174 | M[i][L[i-1]] += 1
175 | M[i][R[i-1]] += 1
176 | graph[i] = [L[i-1], R[i-1]]
177 |
178 | Mh_powers, Mh_power_series = {}, {}
179 | Mh_powers[1], Mh_power_series[1] = build_powers_and_power_series(N, M, INF, B) # Time: O(N^3 * logB)
180 | h1 = get_depth(N, Mh_powers[1], Mh_power_series[1], INF, e(ROOT_COLOR, N), A)
181 | h2 = get_depth(N, Mh_powers[1], Mh_power_series[1], INF, e(ROOT_COLOR, N), B)
182 | x1 = A-sum(get_v_M_power_series_x(N, Mh_powers[1], Mh_power_series[1], INF, e(ROOT_COLOR, N), h1-1))-1
183 | x2 = B-sum(get_v_M_power_series_x(N, Mh_powers[1], Mh_power_series[1], INF, e(ROOT_COLOR, N), h2-1))-1
184 | cycle_adj, cycle_length = find_cycles(graph)
185 | c = ROOT_COLOR
186 | while (h1, x1) != (0, 0):
187 | if c not in cycle_adj: # enter none-only-1-cycle node, Time: O(N^2 * logB) => Total Time: O(N^2 * (logB)^2)
188 | side1, new_x1 = get_single_step_position(Mh_powers[1], INF, e(L[c-1], N), h1, x1)
189 | side2, new_x2 = get_single_step_position(Mh_powers[1], INF, e(L[c-1], N), h2, x2)
190 | if side1 != side2: # found lca
191 | break
192 | h1, x1 = h1-1, new_x1
193 | h2, x2 = h2-1, new_x2
194 | c = (L[c-1] if side1 == LEFT else R[c-1])
195 | continue
196 | # path from root to lca enter a new unseen cycle, we can speed up in this part of path,
197 | # we run this multiple steps solution only if all reachable colors belong to at most one cycle,
198 | # otherwise, the binary tree grows exponentially with height at most O(logB) so that single step solution is fast enough,
199 | # and also saves the extra time and space cost from multiple steps solution
200 | h = cycle_length[c]
201 | if h not in Mh_powers: # lazy init, sum(distinct h) = N => distinct h at most O(sqrt(N)) times, each Time: O(N^3 * logh + N^3 * log(hi)) => Total Time: O(N^3.5 * logN + (N^3.5 * log(logB) + N^3 * logB)) = O(N^3.5 * logN + N^3 * logB) assumed O(N) = O(logB)
202 | Mh_powers[h], Mh_power_series[h] = build_powers_and_power_series(N, get_V_M_power_x(Mh_powers[1], INF, identity_matrix(N), h), INF, min(h1, h2))
203 | cycle, v = [], [0]*N
204 | for x in reversed(range(h)): # Time: O(h * N^2 * logN) => Total Time O(N^3 * logN)
205 | if cycle_adj[c] == R[c-1]:
206 | v = matrix_add([v], [get_V_M_power_x(Mh_powers[1], INF, [e(L[c-1], N)], x)[0]], INF)[0]
207 | c = cycle_adj[c]
208 | cycle.append(c)
209 | p, logp = 1, 0
210 | while (p*2)*h <= min(h1, h2):
211 | p, logp = p*2, logp+1
212 | while p: # logp times => Total Time: O(k cycles * logp times * (N^2 * log(delta_h))) = O(N^3 * log(logB)^2 + N^2 * (logB)^2) = O(N^3 * logB) assumed O(N) = O(logB)
213 | if p*h <= min(h1, h2):
214 | ok1, new_x1 = get_multiple_steps_position(Mh_powers[1], Mh_power_series[h], INF, logp, v, h1-p*h, e(c, N), x1)
215 | ok2, new_x2 = get_multiple_steps_position(Mh_powers[1], Mh_power_series[h], INF, logp, v, h2-p*h, e(c, N), x2)
216 | if ok1 and ok2:
217 | h1, x1 = h1-p*h, new_x1
218 | h2, x2 = h2-p*h, new_x2
219 | p, logp = p//2, logp-1
220 | for x in cycle: # no need to do multiple steps solution
221 | del cycle_adj[x]
222 | return h1+h2
223 |
224 | LEFT, RIGHT = range(2)
225 | LEAF_COLOR, ROOT_COLOR = range(2) # leaf color is 0, root color is 1
226 | for case in range(int(input())):
227 | print('Case #%d: %s' % (case+1, infinitree()))
228 |
--------------------------------------------------------------------------------
/Virtual World Finals/infinitree_concise.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Virtual World Finals - Problem E. Infinitree
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000436329/000000000084fc01
5 | #
6 | # Time: O(N^3.5 * logN + N^3 * logB), pass in PyPy2 but Python2
7 | # Space: O(N^2.5 * logN + N^2 * logB)
8 | #
9 | # concise solution of infinitree.py
10 | #
11 |
12 | from itertools import izip
13 |
14 | # Template modified from:
15 | # https://github.com/kamyu104/GoogleCodeJam-2017/blob/master/Round%202/beaming_with_joy.py
16 | def strongly_connected_components(graph): # Time: O(|V| + |E|) = O(N + 2N) = O(N), Space: O(|V|) = O(N)
17 | def strongconnect(v, index_counter, index, lowlinks, stack, stack_set, result):
18 | index[v] = index_counter[0]
19 | lowlinks[v] = index_counter[0]
20 | index_counter[0] += 1
21 | stack_set.add(v)
22 | stack.append(v)
23 | for w in (graph[v] if v in graph else []):
24 | if w not in index:
25 | strongconnect(w, index_counter, index, lowlinks, stack, stack_set, result)
26 | lowlinks[v] = min(lowlinks[v], lowlinks[w])
27 | elif w in stack_set:
28 | lowlinks[v] = min(lowlinks[v], index[w])
29 | if lowlinks[v] == index[v]:
30 | connected_component = []
31 | w = None
32 | while w != v:
33 | w = stack.pop()
34 | stack_set.remove(w)
35 | connected_component.append(w)
36 | result.append(set(connected_component))
37 |
38 | index_counter, index, lowlinks = [0], {}, {}
39 | stack, stack_set = [], set()
40 | result = []
41 | strongconnect(ROOT_COLOR, index_counter, index, lowlinks, stack, stack_set, result) # modified, only care about reachable colors
42 | return result
43 |
44 | # return cycle_adj, cycle_length only if all reachable colors belong to at most one cycle
45 | def find_cycles(graph): # Time: O(N), Space: O(N)
46 | cycle_adj, cycle_length = {}, {}
47 | for scc in strongly_connected_components(graph):
48 | if any(sum(int(x in scc) for x in graph[node]) == 2 for node in scc): # this is optional optimization
49 | return {}, {} # have a reachable color belonging to more than one cycle, we only need to run single step solution
50 | if any(sum(int(x in scc) for x in graph[node]) != 1 for node in scc):
51 | continue
52 | node = next(iter(scc))
53 | for _ in xrange(len(scc)):
54 | cycle_adj[node] = next(x for x in graph[node] if x in scc)
55 | cycle_length[node] = len(scc)
56 | node = cycle_adj[node]
57 | return cycle_adj, cycle_length
58 |
59 | def floor_log2_x(x): # Time: O(logx)
60 | return x.bit_length()-1
61 |
62 | def identity_matrix(N): # Time: O(N)
63 | return [[int(i == j) for j in xrange(N)] for i in xrange(N)]
64 |
65 | def e(i, N): # Time: O(N)
66 | return [int(j == i) for j in xrange(N)]
67 |
68 | def matrix_mult(A, B, INF): # Time: O(N^2) if A is a 1 x N matrix and B is a N x N matrix, O(N^3) if A, B are both N x N matrixs
69 | result = [[0]*len(B[0]) for _ in xrange(len(A))]
70 | B_T = zip(*B)
71 | for result_i, A_i in izip(result, A):
72 | for j, B_T_i in enumerate(B_T):
73 | for A_i_j, B_T_i_j in izip(A_i, B_T_i):
74 | result_i[j] += A_i_j*B_T_i_j
75 | if result_i[j] > INF:
76 | result_i[j] = INF
77 | break
78 | return result
79 |
80 | def matrix_add(A, B, INF): # Time: O(N) if A, B are both 1 x N matrixs, O(N^2) if A, B are both N x N matrixs
81 | result = [[0]*len(B[0]) for _ in xrange(len(A))]
82 | for result_i, A_i, B_i in izip(result, A, B):
83 | for j in xrange(len(result_i)):
84 | result_i[j] = A_i[j]+B_i[j]
85 | if result_i[j] > INF:
86 | result_i[j] = INF
87 | return result
88 |
89 | # build [M, M^2, ..., M^(2^logx)] and [I, (I + M), (I + M + M^2 + M^3), (I + M + ... + M^(2^logx-1))]
90 | def build_powers_and_power_series(N, M, INF, x): # Time: O(N^3 * logx)
91 | logx = floor_log2_x(x)
92 | I = identity_matrix(N)
93 | # M_powers[i] for i in xrange(1+logx):
94 | # 0: M
95 | # 1: M^2
96 | # ...
97 | # logx: M^(2^logx)
98 | M_powers = [M]
99 | for _ in xrange(logx): # Time: O(N^3 * logx)
100 | M_powers.append(matrix_mult(M_powers[-1], M_powers[-1], INF))
101 | # M_power_series[i] for i in xrange(1+logx):
102 | # 0: I
103 | # 1: (I + M) * I = I + M
104 | # 2: (I + M^2) * (I + M) = I + M + M^2 + M^3
105 | # ...
106 | # logx: (I + M^(2^(logx-1))) * (I + M + ... + M^(2^(logx-1)-1)) = I + M + ... + M^(2^logx-1)
107 | M_power_series = [I]
108 | for i in xrange(logx): # Time: O(N^3 * logx)
109 | matrix = matrix_add(I, M_powers[i], INF)
110 | M_power_series.append(matrix_mult(matrix, M_power_series[-1], INF))
111 | return M_powers, M_power_series
112 |
113 | # V * M^x by vector-matrix or matrix-matrix exponentiation
114 | def get_V_M_power_x(M_powers, INF, V, x): # Time: O(N^2 * logx) if V is a 1 x N matrix, O(N^3 * logx) if V is a N x N matrix
115 | basis, i = 1, 0
116 | while basis <= x:
117 | if x&basis:
118 | V = matrix_mult(V, M_powers[i], INF)
119 | basis, i = basis<<1, i+1
120 | return V
121 |
122 | # v * (I + M + M^2 + ... + M^x) by vector-matrix exponentiation
123 | def get_v_M_power_series_x(N, M_powers, M_power_series, INF, v, x): # Time: O(N^2 * logx)
124 | x += 1
125 | u = [0]*N
126 | basis, i = 1, 0
127 | while basis <= x:
128 | if x&basis:
129 | # new_Pr = P_r*M^(2^i) + P_(2^i)
130 | # new_u = v * new_Pr = v * (P_r*M^(2^i) + P_(2^i)) = u*M^(2^i) + v*P_(2^i)
131 | u = matrix_add(matrix_mult([u], M_powers[i], INF),
132 | matrix_mult([v], M_power_series[i], INF),
133 | INF)[0]
134 | basis, i = basis<<1, i+1
135 | return u
136 |
137 | def get_depth(N, M_powers, M_power_series, INF, v, x): # Time: O(N^2 * logx)
138 | logx = floor_log2_x(x)
139 | result = 0
140 | u = [0]*N
141 | basis = 1<>= 1
153 | return result
154 |
155 | def get_single_step_position(M_powers, INF, ec, h, x): # Time: O(N^2 * logx)
156 | left_cnt = sum(get_V_M_power_x(M_powers, INF, [ec], h-1)[0])
157 | return (LEFT, x) if x < left_cnt else (RIGHT, x-left_cnt)
158 |
159 | def get_multiple_steps_position(M_powers, Mh_power_series, INF, logp, v, delta_h, ec, x): # Time: O(N^2 * log(delta_h))
160 | left_cnt = sum(get_V_M_power_x(M_powers, INF, matrix_mult([v], Mh_power_series[logp], INF), delta_h)[0])
161 | mid_cnt = sum(get_V_M_power_x(M_powers, INF, [ec], delta_h)[0])
162 | return 0 <= x-left_cnt < mid_cnt, x-left_cnt
163 |
164 | def infinitree():
165 | N, A, B = map(int, raw_input().strip().split())
166 | L = map(int, raw_input().strip().split())
167 | R = map(int, raw_input().strip().split())
168 |
169 | N += 1
170 | if A > B:
171 | A, B, = B, A
172 | INF = B
173 | M = [[0]*N for _ in xrange(N)]
174 | graph = {LEAF_COLOR:[]}
175 | for i in xrange(ROOT_COLOR, N):
176 | M[i][L[i-1]] += 1
177 | M[i][R[i-1]] += 1
178 | graph[i] = [L[i-1], R[i-1]]
179 |
180 | Mh_powers, Mh_power_series = {}, {}
181 | Mh_powers[1], Mh_power_series[1] = build_powers_and_power_series(N, M, INF, B) # Time: O(N^3 * logB)
182 | h1 = get_depth(N, Mh_powers[1], Mh_power_series[1], INF, e(ROOT_COLOR, N), A)
183 | h2 = get_depth(N, Mh_powers[1], Mh_power_series[1], INF, e(ROOT_COLOR, N), B)
184 | x1 = A-sum(get_v_M_power_series_x(N, Mh_powers[1], Mh_power_series[1], INF, e(ROOT_COLOR, N), h1-1))-1
185 | x2 = B-sum(get_v_M_power_series_x(N, Mh_powers[1], Mh_power_series[1], INF, e(ROOT_COLOR, N), h2-1))-1
186 | cycle_adj, cycle_length = find_cycles(graph)
187 | c = ROOT_COLOR
188 | while (h1, x1) != (0, 0):
189 | if c not in cycle_adj: # enter none-only-1-cycle node, Time: O(N^2 * logB) => Total Time: O(N^2 * (logB)^2)
190 | side1, new_x1 = get_single_step_position(Mh_powers[1], INF, e(L[c-1], N), h1, x1)
191 | side2, new_x2 = get_single_step_position(Mh_powers[1], INF, e(L[c-1], N), h2, x2)
192 | if side1 != side2: # found lca
193 | break
194 | h1, x1 = h1-1, new_x1
195 | h2, x2 = h2-1, new_x2
196 | c = (L[c-1] if side1 == LEFT else R[c-1])
197 | continue
198 | # path from root to lca enter a new unseen cycle, we can speed up in this part of path,
199 | # we run this multiple steps solution only if all reachable colors belong to at most one cycle,
200 | # otherwise, the binary tree grows exponentially with height at most O(logB) so that single step solution is fast enough,
201 | # and also saves the extra time and space cost from multiple steps solution
202 | h = cycle_length[c]
203 | if h not in Mh_powers: # lazy init, sum(distinct h) = N => distinct h at most O(sqrt(N)) times, each Time: O(N^3 * logh + N^3 * log(hi)) => Total Time: O(N^3.5 * logN + (N^3.5 * log(logB) + N^3 * logB)) = O(N^3.5 * logN + N^3 * logB) assumed O(N) = O(logB)
204 | Mh_powers[h], Mh_power_series[h] = build_powers_and_power_series(N, get_V_M_power_x(Mh_powers[1], INF, identity_matrix(N), h), INF, min(h1, h2))
205 | cycle, v = [], [0]*N
206 | for x in reversed(xrange(h)): # Time: O(h * N^2 * logN) => Total Time O(N^3 * logN)
207 | if cycle_adj[c] == R[c-1]:
208 | v = matrix_add([v], [get_V_M_power_x(Mh_powers[1], INF, [e(L[c-1], N)], x)[0]], INF)[0]
209 | c = cycle_adj[c]
210 | cycle.append(c)
211 | p, logp = 1, 0
212 | while (p*2)*h <= min(h1, h2):
213 | p, logp = p*2, logp+1
214 | while p: # logp times => Total Time: O(k cycles * logp times * (N^2 * log(delta_h))) = O(N^3 * log(logB)^2 + N^2 * (logB)^2) = O(N^3 * logB) assumed O(N) = O(logB)
215 | if p*h <= min(h1, h2):
216 | ok1, new_x1 = get_multiple_steps_position(Mh_powers[1], Mh_power_series[h], INF, logp, v, h1-p*h, e(c, N), x1)
217 | ok2, new_x2 = get_multiple_steps_position(Mh_powers[1], Mh_power_series[h], INF, logp, v, h2-p*h, e(c, N), x2)
218 | if ok1 and ok2:
219 | h1, x1 = h1-p*h, new_x1
220 | h2, x2 = h2-p*h, new_x2
221 | p, logp = p//2, logp-1
222 | for x in cycle: # no need to do multiple steps solution
223 | del cycle_adj[x]
224 | return h1+h2
225 |
226 | LEFT, RIGHT = xrange(2)
227 | LEAF_COLOR, ROOT_COLOR = xrange(2) # leaf color is 0, root color is 1
228 | for case in xrange(input()):
229 | print 'Case #%d: %s' % (case+1, infinitree())
230 |
--------------------------------------------------------------------------------
/Virtual World Finals/infinitree.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2021 kamyu. All rights reserved.
2 | #
3 | # Google Code Jam 2021 Virtual World Finals - Problem E. Infinitree
4 | # https://codingcompetitions.withgoogle.com/codejam/round/0000000000436329/000000000084fc01
5 | #
6 | # Time: O(N^3.5 * logN + N^3 * logB), pass in PyPy2 but Python2
7 | # Space: O(N^2.5 * logN + N^2 * logB)
8 | #
9 |
10 | from itertools import izip
11 |
12 | # Template modified from:
13 | # https://github.com/kamyu104/GoogleCodeJam-2017/blob/master/Round%202/beaming_with_joy.py
14 | def strongly_connected_components(graph): # Time: O(|V| + |E|) = O(N + 2N) = O(N), Space: O(|V|) = O(N)
15 | def strongconnect(v, index_counter, index, lowlinks, stack, stack_set, result):
16 | index[v] = index_counter[0]
17 | lowlinks[v] = index_counter[0]
18 | index_counter[0] += 1
19 | stack_set.add(v)
20 | stack.append(v)
21 | for w in (graph[v] if v in graph else []):
22 | if w not in index:
23 | strongconnect(w, index_counter, index, lowlinks, stack, stack_set, result)
24 | lowlinks[v] = min(lowlinks[v], lowlinks[w])
25 | elif w in stack_set:
26 | lowlinks[v] = min(lowlinks[v], index[w])
27 | if lowlinks[v] == index[v]:
28 | connected_component = []
29 | w = None
30 | while w != v:
31 | w = stack.pop()
32 | stack_set.remove(w)
33 | connected_component.append(w)
34 | result.append(set(connected_component))
35 |
36 | index_counter, index, lowlinks = [0], {}, {}
37 | stack, stack_set = [], set()
38 | result = []
39 | strongconnect(ROOT_COLOR, index_counter, index, lowlinks, stack, stack_set, result) # modified, only care about reachable colors
40 | return result
41 |
42 | # return cycle_adj, cycle_length only if all reachable colors belong to at most one cycle
43 | def find_cycles(graph): # Time: O(N), Space: O(N)
44 | cycle_adj, cycle_length = {}, {}
45 | for scc in strongly_connected_components(graph):
46 | if any(sum(int(x in scc) for x in graph[node]) == 2 for node in scc): # this is optional optimization
47 | return {}, {} # have a reachable color belonging to more than one cycle, we only need to run single step solution
48 | if any(sum(int(x in scc) for x in graph[node]) != 1 for node in scc):
49 | continue
50 | node = next(iter(scc))
51 | for _ in xrange(len(scc)):
52 | cycle_adj[node] = next(x for x in graph[node] if x in scc)
53 | cycle_length[node] = len(scc)
54 | node = cycle_adj[node]
55 | return cycle_adj, cycle_length
56 |
57 | def floor_log2_x(x): # Time: O(logx)
58 | return x.bit_length()-1
59 |
60 | def identity_matrix(N): # Time: O(N)
61 | return [[int(i == j) for j in xrange(N)] for i in xrange(N)]
62 |
63 | def e(i, N): # Time: O(N)
64 | return [int(j == i) for j in xrange(N)]
65 |
66 | def vector_mult(A, B, INF): # Time: O(N^2), A is a N-d vector, B is a N x N matrix
67 | result = [0]*len(B[0])
68 | B_T = zip(*B)
69 | for i, B_T_i in enumerate(B_T):
70 | for _, (A_j, B_T_i_j) in enumerate(izip(A, B_T_i)):
71 | result[i] += A_j*B_T_i_j
72 | if result[i] > INF:
73 | result[i] = INF
74 | break
75 | return result
76 |
77 | def matrix_mult(A, B, INF): # Time: O(N^3), A, B are both N x N matrixs
78 | result = [[0]*len(B[0]) for _ in xrange(len(A))]
79 | B_T = zip(*B)
80 | for result_i, A_i in izip(result, A):
81 | for j, B_T_i in enumerate(B_T):
82 | for A_i_j, B_T_i_j in izip(A_i, B_T_i):
83 | result_i[j] += A_i_j*B_T_i_j
84 | if result_i[j] > INF:
85 | result_i[j] = INF
86 | break
87 | return result
88 |
89 | def vector_add(A, B, INF): # Time: O(N)
90 | result = [0]*len(A)
91 | for i, (A_i, B_i) in enumerate(izip(A, B)):
92 | result[i] = A_i+B_i
93 | if result[i] > INF:
94 | result[i] = INF
95 | return result
96 |
97 | def matrix_add(A, B, INF): # Time: O(N^2)
98 | result = [[0]*len(B[0]) for _ in xrange(len(A))]
99 | for result_i, A_i, B_i in izip(result, A, B):
100 | for j in xrange(len(result_i)):
101 | result_i[j] = A_i[j]+B_i[j]
102 | if result_i[j] > INF:
103 | result_i[j] = INF
104 | return result
105 |
106 | # build [M, M^2, ..., M^(2^logx)] and [I, (I + M), (I + M + M^2 + M^3), (I + M + ... + M^(2^logx-1))]
107 | def build_powers_and_power_series(N, M, INF, x): # Time: O(N^3 * logx)
108 | logx = floor_log2_x(x)
109 | I = identity_matrix(N)
110 | # M_powers[i] for i in xrange(1+logx):
111 | # 0: M
112 | # 1: M^2
113 | # ...
114 | # logx: M^(2^logx)
115 | M_powers = [M]
116 | for _ in xrange(logx): # Time: O(N^3 * logx)
117 | M_powers.append(matrix_mult(M_powers[-1], M_powers[-1], INF))
118 | # M_power_series[i] for i in xrange(1+logx):
119 | # 0: I
120 | # 1: (I + M) * I = I + M
121 | # 2: (I + M^2) * (I + M) = I + M + M^2 + M^3
122 | # ...
123 | # logx: (I + M^(2^(logx-1))) * (I + M + ... + M^(2^(logx-1)-1)) = I + M + ... + M^(2^logx-1)
124 | M_power_series = [I]
125 | for i in xrange(logx): # Time: O(N^3 * logx)
126 | matrix = matrix_add(I, M_powers[i], INF)
127 | M_power_series.append(matrix_mult(matrix, M_power_series[-1], INF))
128 | return M_powers, M_power_series
129 |
130 | # V * M^x by matrix exponentiation
131 | def get_V_M_power_x(M_powers, INF, V, x): # Time: O(N^3 * logx)
132 | basis, i = 1, 0
133 | while basis <= x:
134 | if x&basis:
135 | V = matrix_mult(V, M_powers[i], INF)
136 | basis, i = basis<<1, i+1
137 | return V
138 |
139 | # v * M^x by vector-matrix exponentiation
140 | def get_v_M_power_x(M_powers, INF, v, x): # Time: O(N^2 * logx)
141 | basis, i = 1, 0
142 | while basis <= x:
143 | if x&basis:
144 | v = vector_mult(v, M_powers[i], INF)
145 | basis, i = basis<<1, i+1
146 | return v
147 |
148 | # v * (I + M + M^2 + ... + M^x) by vector-matrix exponentiation
149 | def get_v_M_power_series_x(N, M_powers, M_power_series, INF, v, x): # Time: O(N^2 * logx)
150 | x += 1
151 | u = [0]*N
152 | basis, i = 1, 0
153 | while basis <= x:
154 | if x&basis:
155 | # new_Pr = P_r*M^(2^i) + P_(2^i)
156 | # new_u = v * new_Pr = v * (P_r*M^(2^i) + P_(2^i)) = u*M^(2^i) + v*P_(2^i)
157 | u = vector_add(vector_mult(u, M_powers[i], INF),
158 | vector_mult(v, M_power_series[i], INF),
159 | INF)
160 | basis, i = basis<<1, i+1
161 | return u
162 |
163 | def get_depth(N, M_powers, M_power_series, INF, v, x): # Time: O(N^2 * logx)
164 | logx = floor_log2_x(x)
165 | result = 0
166 | u = [0]*N
167 | basis = 1<>= 1
179 | return result
180 |
181 | def get_single_step_position(M_powers, INF, ec, h, x): # Time: O(N^2 * logx)
182 | left_cnt = sum(get_v_M_power_x(M_powers, INF, ec, h-1))
183 | return (LEFT, x) if x < left_cnt else (RIGHT, x-left_cnt)
184 |
185 | def get_multiple_steps_position(M_powers, Mh_power_series, INF, logp, v, delta_h, ec, x): # Time: O(N^2 * log(delta_h))
186 | left_cnt = sum(get_v_M_power_x(M_powers, INF, vector_mult(v, Mh_power_series[logp], INF), delta_h))
187 | mid_cnt = sum(get_v_M_power_x(M_powers, INF, ec, delta_h))
188 | return 0 <= x-left_cnt < mid_cnt, x-left_cnt
189 |
190 | def infinitree():
191 | N, A, B = map(int, raw_input().strip().split())
192 | L = map(int, raw_input().strip().split())
193 | R = map(int, raw_input().strip().split())
194 |
195 | N += 1
196 | if A > B:
197 | A, B, = B, A
198 | INF = B
199 | M = [[0]*N for _ in xrange(N)]
200 | graph = {LEAF_COLOR:[]}
201 | for i in xrange(ROOT_COLOR, N):
202 | M[i][L[i-1]] += 1
203 | M[i][R[i-1]] += 1
204 | graph[i] = [L[i-1], R[i-1]]
205 |
206 | Mh_powers, Mh_power_series = {}, {}
207 | Mh_powers[1], Mh_power_series[1] = build_powers_and_power_series(N, M, INF, B) # Time: O(N^3 * logB)
208 | h1 = get_depth(N, Mh_powers[1], Mh_power_series[1], INF, e(ROOT_COLOR, N), A)
209 | h2 = get_depth(N, Mh_powers[1], Mh_power_series[1], INF, e(ROOT_COLOR, N), B)
210 | x1 = A-sum(get_v_M_power_series_x(N, Mh_powers[1], Mh_power_series[1], INF, e(ROOT_COLOR, N), h1-1))-1
211 | x2 = B-sum(get_v_M_power_series_x(N, Mh_powers[1], Mh_power_series[1], INF, e(ROOT_COLOR, N), h2-1))-1
212 | cycle_adj, cycle_length = find_cycles(graph)
213 | c = ROOT_COLOR
214 | while (h1, x1) != (0, 0):
215 | if c not in cycle_adj: # enter none-only-1-cycle node, Time: O(N^2 * logB) => Total Time: O(N^2 * (logB)^2)
216 | side1, new_x1 = get_single_step_position(Mh_powers[1], INF, e(L[c-1], N), h1, x1)
217 | side2, new_x2 = get_single_step_position(Mh_powers[1], INF, e(L[c-1], N), h2, x2)
218 | if side1 != side2: # found lca
219 | break
220 | h1, x1 = h1-1, new_x1
221 | h2, x2 = h2-1, new_x2
222 | c = (L[c-1] if side1 == LEFT else R[c-1])
223 | continue
224 | # path from root to lca enter a new unseen cycle, we can speed up in this part of path,
225 | # we run this multiple steps solution only if all reachable colors belong to at most one cycle,
226 | # otherwise, the binary tree grows exponentially with height at most O(logB) so that single step solution is fast enough,
227 | # and also saves the extra time and space cost from multiple steps solution
228 | h = cycle_length[c]
229 | if h not in Mh_powers: # lazy init, sum(distinct h) = N => distinct h at most O(sqrt(N)) times, each Time: O(N^3 * logh + N^3 * log(hi)) => Total Time: O(N^3.5 * logN + (N^3.5 * log(logB) + N^3 * logB)) = O(N^3.5 * logN + N^3 * logB) assumed O(N) = O(logB)
230 | Mh_powers[h], Mh_power_series[h] = build_powers_and_power_series(N, get_V_M_power_x(Mh_powers[1], INF, identity_matrix(N), h), INF, min(h1, h2))
231 | cycle, v = [], [0]*N
232 | for x in reversed(xrange(h)): # Time: O(h * N^2 * logN) => Total Time O(N^3 * logN)
233 | if cycle_adj[c] == R[c-1]:
234 | v = vector_add(v, get_v_M_power_x(Mh_powers[1], INF, e(L[c-1], N), x), INF)
235 | c = cycle_adj[c]
236 | cycle.append(c)
237 | p, logp = 1, 0
238 | while (p*2)*h <= min(h1, h2):
239 | p, logp = p*2, logp+1
240 | while p: # logp times => Total Time: O(k cycles * logp times * (N^2 * log(delta_h))) = O(N^3 * log(logB)^2 + N^2 * (logB)^2) = O(N^3 * logB) assumed O(N) = O(logB)
241 | if p*h <= min(h1, h2):
242 | ok1, new_x1 = get_multiple_steps_position(Mh_powers[1], Mh_power_series[h], INF, logp, v, h1-p*h, e(c, N), x1)
243 | ok2, new_x2 = get_multiple_steps_position(Mh_powers[1], Mh_power_series[h], INF, logp, v, h2-p*h, e(c, N), x2)
244 | if ok1 and ok2:
245 | h1, x1 = h1-p*h, new_x1
246 | h2, x2 = h2-p*h, new_x2
247 | p, logp = p//2, logp-1
248 | for x in cycle: # no need to do multiple steps solution
249 | del cycle_adj[x]
250 | return h1+h2
251 |
252 | LEFT, RIGHT = xrange(2)
253 | LEAF_COLOR, ROOT_COLOR = xrange(2) # leaf color is 0, root color is 1
254 | for case in xrange(input()):
255 | print 'Case #%d: %s' % (case+1, infinitree())
256 |
--------------------------------------------------------------------------------