simulate
accumlate
greedy
sort
math
nnt
generating functions
divide and conquer
graph
divide and conquer
spanning tree
]
Codeforces contest 0632
Problem A statement
[simulate]
https://codeforces.com/contest/632/problem/A
Solution
Simulate process from the end.
Complexity
Time and space complexity is O(n)
.
Code
n, p = [int(i) for i in input().split()]
ans = 0
free = 0
arr = [input().rstrip()[-1] == "s" for _ in range(n)]
for x in arr[::-1]:
if x:
ans = ans * 2 + 1
free += 1
else:
ans *= 2
print(ans*p - p//2*free)
Problem B statement
[accumulate, greedy]
https://codeforces.com/contest/632/problem/B
Solution
We need to find prefix or suffix, where diff between A
and B
elements are the biggest. So, create sm
array, where all elements of A
with plus sign and B
with minus sign.
Complexity
It is O(n)
for time and space.
Code
from itertools import accumulate
n = int(input())
arr = [int(i) for i in input().split()]
s = input().rstrip()
sm = [i*(1 - 2*(x == "B")) for i, x in zip(arr, s)]
acc1 = [0] + list(accumulate(sm))
acc2 = [0] + list(accumulate(sm[::-1]))
t = max(max(acc1), max(acc2))
print(sum(i* (x=="B") for i, x in zip(arr, s)) + t)
Problem C statement
[sort, greedy]
https://codeforces.com/contest/632/problem/C
Solution
Here we need to use custom sort: it is equal to Leetcode 0179
Complexity
It is O(n log n * x)
, where n
is the length of strings.
Code
from functools import cmp_to_key
n = int(input())
strs = [input().rstrip() for _ in range(n)]
compare = lambda a, b: -1 if a+b < b+a else 1 if a+b > b+a else 0
print("".join(sorted(strs, key = cmp_to_key(compare))))
Problem D statement
[math]
https://codeforces.com/contest/632/problem/D
Solution
The idea is for each number x
find all numbers which are divisible by x
, that is x, 2x, 3x, ...
. When we iterate over x = 1, 2, ..., m
we will do at most O(m log m)
operations. To make it faster in the beginning we can evaluate counter of our numbers and then when we iterate we do divs[j] += cnt[i]
. Then we find the maximum from divs
array and take the first value with this value. It is important to take the first one, because for example for array [2, 2, 2, 2, 3, 3, 3]
, divs[6] = divs[12] = 7
. Then after we found this value, iterate over numbers once again and add only elements which divide this lcm
.
Complexity
It is O(m log m + n)
for time and O(m)
for space.
Code
from collections import Counter
import io, os, sys
input = io.BytesIO(os.read(0,os.fstat(0).st_size)).readline
I = lambda: [int(i) for i in input().split()]
n, m = I()
arr = I()
cnt = Counter(arr)
divs = [0] * (m + 1)
for i in range(1, m + 1):
for j in range(i, m + 1, i):
divs[j] += cnt[i]
lcm = max(range(1, m+1), key = divs.__getitem__)
out = []
for i, a in enumerate(arr):
if lcm % a == 0: out += [i]
print(str(lcm) + " " + str(len(out)))
print(" ".join(str(x+1) for x in out))
Problem E statement
[nnt, generating functions, divide and conquer]
https://codeforces.com/contest/632/problem/E
Solution
Imagine, that we have costs 3, 5, 11
. Then, let us create polynom x^3 + x^5 + x^11
. Then what we need to do is take it to the power k
and take only those elements which are not equal to 0
. We can do it with nnt (or here with fft, because coefficients are small enough),
Complexity
It is O(N*log^2N)
, where N = max(a) * k < 1000000
Code
MOD, ROOT = 998244353, 3
I = lambda: [int(i) for i in input().split()]
# IMPORT NNT FROM TEMPLATES
n, k = I()
A = I()
N = max(A) + 1
pol = [0]*N
for x in A: pol[x] = 1
arr = [pol[:] for _ in range(k)]
def prod(l):
n = len(l)
if n == 1: return l[0]
lft = prod(l[:n//2])
rgh = prod(l[n//2:])
ntt_conv(lft, rgh)
return [int(i != 0) for i in lft]
pol = prod(arr)
ans = [i for i, x in enumerate(pol) if x == 1]
print(" ".join(str(x) for x in ans))
Solution 2
In fact, complexity can be improved: we can do it in O(N log N)
, because previously we had T(n) = 2*T(n/2) + n log n
complexity and in fact we can do it just in T(n) = T(n/2) + n log n
, because we have two subproblems which are the same. In practice it will give only twice speed gain.
Code 2
MOD, ROOT = 998244353, 3
I = lambda: [int(i) for i in input().split()]
# IMPORT NNT FROM TEMPLATES
n, k = I()
A = I()
N = max(A) + 1
pol = [0]*N
for x in A: pol[x] = 1
def power(pol, n):
result = [1]
while n > 0:
if n % 2: ntt_conv(result, pol[:])
result = [int(i != 0) for i in result]
x, y = pol[:], pol[:]
ntt_conv(x, y)
n //= 2
pol = [int(i != 0) for i in x]
return result
pol = power(pol, k)
ans = [i for i, x in enumerate(pol) if x == 1]
print(" ".join(str(x) for x in ans))
Problem F statement
[graph, divide and conquer, MST]
https://codeforces.com/contest/632/problem/F
Solution
First of all notice that we can look at this problem as at graph. Also inequalities in the problem statement means that for every 3
nodes a, b, c
: d(a, b) <= max(d(a, c), d(b, c))
. If we write down these conditions for (a, b, c)
, (b, c, a)
and (c, a, b)
then we see that we can have only triples of distances of the form x, y, y
, where x <= y
. That is we can not have say 1, 2, 3
or 1, 1, 3
, but we can have 1, 3, 3
.
Next step is for fixed node look at all edges going from this node and split elements into groups with equal edge weight. That is if we have in the first row [0, 4, 4, 4, 5, 4, 3, 3, 5, 4]
, then we split into groups 3: (6, 7)
, 4: (1, 2, 3, 5, 9)
, 5: (4, 8)
and . Now we want to understand which edges we can have between and inside groups. In fact two conditions need to be hold:
- Between groups with edges values
x
andy
, wherex < y
we must have edge with weighty
. Indeed, we have triangle, where we already have(x, y)
and the remaining side can be onlyy
. - Inside each group we must have values, which is smaller or equal than value of edge going to this group. Again, we have triangle with edges
(x, x)
and the reset value should be<= x
. - In fact these two conditions are sufficient and enough! It can be proven, considering different type of triangles.
So, we can use divide and conquer idea, where `check(arr, LIM):
arr
is array if indexes we consider.LIM
is maximum limit of value of edge we allowed.- first output is True/False: is our matrix magic/not magic
-
second output is maximum value of edge for given
arr
of indexes. - First, we check if
len(arr) == 1
, then we return(True, 0)
. - Then we choose pivot as first element of
arr
and separate into groups. - Then we go through sorted keys and check condition 1, if it is broken, return False and not matter what as second value.
- Then we go through subproblems and again if we have False somewhere, return False and anything.
- Also we update
max_val
: maximum value of edge for given set of indexes. - If
max_val > LIM
, condition 2 is broken and we returnFalse
, in the opposite case returnTrue
and maximum weight.
Complexity
It is O(n^2 log n)
for time and O(n^2)
for space.
Code
from collections import defaultdict
from itertools import combinations, product
import io, os, sys
input = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline
sys.setrecursionlimit(3000)
I = lambda: [int(i) for i in input().split()]
n = int(input())
A = [I() for _ in range(n)]
def check(arr, LIM):
if len(arr) == 1:
return (True, 0)
d = defaultdict(list)
pivot = arr[0]
for x in arr[1:]:
d[A[pivot][x]] += [x]
vals = sorted(d.keys())
for x, y in combinations(vals, 2):
for i1, i2 in product(d[x], d[y]):
if A[i1][i2] != y: return (False, 0)
max_val = vals[-1]
for x in vals:
res, vl = check(d[x], x)
if not res: return (False, 0)
max_val = max(max_val, vl)
return (False, 0) if max_val > LIM else (True, max_val)
def solve():
for i in range(n):
for j in range(i + 1, n):
if A[i][j] != A[j][i]: return False
if A[i][i] != 0: return False
return check(list(range(n)), float("inf"))[0]
print("MAGIC" if solve() else "NOT MAGIC")
Remark
Authors solution of this problem use MST.