def multiply_perm(p1, p2):
assert len(p1) == len(p2)
result = [None] * len(p1)
for i in xrange(len(p1)):
result[i] = p2[p1[i]]
return result
def inverse_perm(p):
result = [None] * len(p)
for i, p_i in enumerate(p):
print i, p_i
result[p_i] = i
assert None not in result
return result
def cycle_decompose_perm(p):
visited = [False] * len(p)
for i in xrange(len(p)):
if visited[i]:
continue
cur_cycle = []
pos = i
while not visited[pos]:
visited[pos] = True
cur_cycle.append(pos)
pos = p[pos]
yield cur_cycle
def gcd(a, b):
if not a and not b:
return 1
while b:
a, b = b, a % b
return a
def lcm(a, b):
return (a / gcd(a, b)) * b
def find_power_of_perm(p):
ans = 1
for cyc in cycle_decompose_perm(p):
ans = lcm(ans, len(cyc))
return ans
assert multiply_perm([0,1,2,3], [1,2,3,0]) == [1,2,3,0]
assert inverse_perm([2,0,1]) == [1,2,0]
assert list(cycle_decompose_perm([1,0,3,2])) == [[0,1], [2,3]]
assert find_power_of_perm([0,1,2,3]) == 1
assert find_power_of_perm([1,0,3,2]) == 2
assert find_power_of_perm([1,2,3,0]) == 4
CmRlZiBtdWx0aXBseV9wZXJtKHAxLCBwMik6Cglhc3NlcnQgbGVuKHAxKSA9PSBsZW4ocDIpCglyZXN1bHQgPSBbTm9uZV0gKiBsZW4ocDEpCglmb3IgaSBpbiB4cmFuZ2UobGVuKHAxKSk6CgkJcmVzdWx0W2ldID0gcDJbcDFbaV1dCglyZXR1cm4gcmVzdWx0CgpkZWYgaW52ZXJzZV9wZXJtKHApOgoJcmVzdWx0ID0gW05vbmVdICogbGVuKHApCglmb3IgaSwgcF9pIGluIGVudW1lcmF0ZShwKToKCQlwcmludCBpLCBwX2kKCQlyZXN1bHRbcF9pXSA9IGkKCWFzc2VydCBOb25lIG5vdCBpbiByZXN1bHQKCXJldHVybiByZXN1bHQKCmRlZiBjeWNsZV9kZWNvbXBvc2VfcGVybShwKToKCXZpc2l0ZWQgPSBbRmFsc2VdICogbGVuKHApCglmb3IgaSBpbiB4cmFuZ2UobGVuKHApKToKCQlpZiB2aXNpdGVkW2ldOgoJCQljb250aW51ZQoJCWN1cl9jeWNsZSA9IFtdCgkJcG9zID0gaQoJCXdoaWxlIG5vdCB2aXNpdGVkW3Bvc106CgkJCXZpc2l0ZWRbcG9zXSA9IFRydWUKCQkJY3VyX2N5Y2xlLmFwcGVuZChwb3MpCgkJCXBvcyA9IHBbcG9zXQoJCXlpZWxkIGN1cl9jeWNsZQoKZGVmIGdjZChhLCBiKToKCWlmIG5vdCBhIGFuZCBub3QgYjoKCQlyZXR1cm4gMQoJd2hpbGUgYjoKCQlhLCBiID0gYiwgYSAlIGIKCXJldHVybiBhCgpkZWYgbGNtKGEsIGIpOgoJcmV0dXJuIChhIC8gZ2NkKGEsIGIpKSAqIGIKCmRlZiBmaW5kX3Bvd2VyX29mX3Blcm0ocCk6CglhbnMgPSAxCglmb3IgY3ljIGluIGN5Y2xlX2RlY29tcG9zZV9wZXJtKHApOgoJCWFucyA9IGxjbShhbnMsIGxlbihjeWMpKQoJcmV0dXJuIGFucwoKYXNzZXJ0IG11bHRpcGx5X3Blcm0oWzAsMSwyLDNdLCBbMSwyLDMsMF0pID09IFsxLDIsMywwXQphc3NlcnQgaW52ZXJzZV9wZXJtKFsyLDAsMV0pID09IFsxLDIsMF0KYXNzZXJ0IGxpc3QoY3ljbGVfZGVjb21wb3NlX3Blcm0oWzEsMCwzLDJdKSkgPT0gW1swLDFdLCBbMiwzXV0KYXNzZXJ0IGZpbmRfcG93ZXJfb2ZfcGVybShbMCwxLDIsM10pID09IDEKYXNzZXJ0IGZpbmRfcG93ZXJfb2ZfcGVybShbMSwwLDMsMl0pID09IDIKYXNzZXJ0IGZpbmRfcG93ZXJfb2ZfcGVybShbMSwyLDMsMF0pID09IDQ=