The Tree Has Fallen! (Codeforces 1778E)
Tags: Math, trees

Recently, a tree has fallen on Bob's head from the sky. The tree has $$$n$$$ nodes. Each node $$$u$$$ of the tree has an integer number $$$a_u$$$ written on it. But the tree has no fixed root, as it has fallen from the sky.Bob is currently studying the tree. To add some twist, Alice proposes a game. First, Bob chooses some node $$$r$$$ to be the root of the tree. After that, Alice chooses a node $$$v$$$ and tells him. Bob then can pick one or more nodes from the subtree of $$$v$$$. His score will be the bitwise XOR of all the values written on the nodes picked by him. Bob has to find the maximum score he can achieve for the given $$$r$$$ and $$$v$$$.As Bob is not a good problem-solver, he asks you to help him find the answer. Can you help him? You need to find the answers for several combinations of $$$r$$$ and $$$v$$$ for the same tree.Recall that a tree is a connected undirected graph without cycles. The subtree of a node $$$u$$$ is the set of all nodes $$$y$$$ such that the simple path from $$$y$$$ to the root passes through $$$u$$$. Note that $$$u$$$ is in the subtree of $$$u$$$.

Input: Each test contains multiple test cases. The first line contains the number of test cases $$$t$$$ ($$$1 \le t \le 10^5$$$). The description of the test cases follows.The first line contains an integer $$$n$$$ ($$$2\leq n\leq 2 \cdot 10^5$$$) — the number of nodes of the tree. The next line contains $$$n$$$ space-separated integers $$$a_1, a_2, \dots, a_n$$$ ($$$1\leq a_i\leq 10^9$$$) — the values written on the nodes. Each of the next $$$n-1$$$ lines contain two integers $$$u$$$ and $$$v$$$ ($$$1\leq u, v\leq n$$$; $$$u\neq v$$$), denoting there is an undirected edge between node $$$u$$$ and node $$$v$$$. It is guaranteed that the given edges form a tree.The next line contains an integer $$$q$$$ ($$$1\leq q\leq 2 \cdot 10^5$$$) — the number of queries. Each of the next $$$q$$$ lines contains two integers $$$r$$$ and $$$v$$$ ($$$1\leq r, v\leq n$$$) — the root node Bob defines, and the node Alice chooses.It is guaranteed that the sum of $$$n$$$ and the sum of $$$q$$$ over all test cases don't exceed $$$2 \cdot 10^5$$$.

Output: For each test case, in each query, print a line containing an integer denoting the maximum score Bob can achieve.

Note: In each of the below figures, the green-colored node is the node picked by Bob, and the red-colored node is the node picked by Alice. The values of the nodes are placed in the blue boxes beside the nodes.Consider the first example. In the first query, if we put the root node $$$4$$$ at the top of the tree, the tree looks like the below figure:   Tree with root node $$$4$$$ in the first query.  The nodes in the subtree of the node $$$2$$$ are $$$[2,1,5,6,3]$$$. Among them, Bob can pick node $$$3$$$, node $$$5$$$, and node $$$6$$$. His score will be $$$a_3 \oplus a_5 \oplus a_6 = 8 \oplus 6 \oplus 1 = 15$$$. He can't achieve any score more than this.In the second query, if the root node $$$3$$$ is placed at the top of the tree, the tree looks like the below figure:   Tree with root node $$$3$$$ in the second query.  The only node in the subtree of the node $$$5$$$ is $$$5$$$. Bob can only pick the node $$$5$$$. His score will be $$$a_5 = 6$$$.In the third query, if the root node $$$1$$$ is placed at the top of the tree, the tree looks like this:   Tree with root node $$$1$$$ in the third query.  The nodes in the subtree of the node $$$2$$$ are $$$[2,3,6,4]$$$. Among them, Bob can pick node $$$2$$$, node $$$3$$$, and node $$$4$$$. His score will be $$$a_2 \oplus a_3 \oplus a_4 = 12 \oplus 8 \oplus 25 = 29$$$. He is not able to score higher than that.

Sample Inputs:
3
6
12 12 8 25 6 1
1 5
1 2
2 6
2 3
2 4
3
4 2
3 5
1 2
2
3 8
1 2
4
2 2
2 1
1 2
1 1
3
3 8 7
1 2
2 3
2
2 2
2 1

Sample Outputs:
15
6
29
11
3
8
11
15
3

Solution (Python):
import sys, os, io
input = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline

def make_graph(n, m):
    (x, s) = ([], [0] * (n + 3))
    for _ in range(m):
        (u, v) = map(int, input().split())
        s[u + 2] += 1
        s[v + 2] += 1
        x.append(u)
        x.append(v)
    for i in range(3, n + 3):
        s[i] += s[i - 1]
    G = [0] * (2 * m)
    for i in range(2 * m):
        j = x[i] + 1
        G[s[j]] = x[i ^ 1]
        s[j] += 1
    return (G, s)

def bfs(s):
    q = [s]
    dist = [inf] * (n + 1)
    dist[s] = 0
    parent = [-1] * (n + 1)
    for k in range(n):
        i = q[k]
        di = dist[i]
        for v in range(s0[i], s0[i + 1]):
            j = G[v]
            if dist[j] == inf:
                q.append(j)
                dist[j] = di + 1
                parent[j] = i
    return (q, parent, dist)

def f(u, v):
    return u * (n + 1) + v
t = int(input())
ans = []
inf = pow(10, 9) + 1
pow2 = [1]
for _ in range(20):
    pow2.append(2 * pow2[-1])
for _ in range(t):
    n = int(input())
    a = [0] + list(map(int, input().split()))
    (G, s0) = make_graph(n, n - 1)
    (p, parent, dist) = bfs(1)
    child = [0] * (n - 1)
    s1 = [0] * (n + 2)
    k = 0
    for i in range(1, n + 1):
        di = dist[i]
        for v in range(s0[i], s0[i + 1]):
            j = G[v]
            if dist[j] > di:
                child[k] = j
                k += 1
        s1[i + 1] = k
    b = [[] for _ in range(n + 1)]
    b[1].append(a[1])
    for i in p[::-1][:-1]:
        u = a[i]
        bi = b[i]
        for v in bi:
            u = min(u, u ^ v)
        if u:
            bi.append(u)
        bj = b[parent[i]]
        for u0 in bi:
            u = u0
            for v in bj:
                u = min(u, u ^ v)
            if u:
                bj.append(u)
    d = dict()
    for i in range(1, n + 1):
        bi = b[i]
        bi.sort(reverse=True)
        u = 0
        for j in bi:
            u = max(u, u ^ j)
        d[f(i, parent[i])] = u
    b0 = [[] for _ in range(n + 1)]
    for i in p:
        ci = [child[j] for j in range(s1[i], s1[i + 1])]
        if not ci:
            continue
        (l, l0) = ([[]], [])
        for j in ci:
            bj = b[j]
            for u0 in bj:
                u = u0
                for v in l0:
                    u = min(u, u ^ v)
                if u:
                    l0.append(u)
            l.append(list(l0))
        (r, r0) = ([[]], [])
        for j in ci[::-1]:
            bj = b[j]
            for u0 in bj:
                u = u0
                for v in r0:
                    u = min(u, u ^ v)
                if u:
                    r0.append(u)
            r.append(list(r0))
        r.reverse()
        for k in range(len(ci)):
            j = ci[k]
            bj = list(b0[i])
            for w in [l[k], r[k + 1], [a[i]]]:
                for u0 in w:
                    u = u0
                    for v in bj:
                        u = min(u, u ^ v)
                    if u:
                        bj.append(u)
            b0[j] = bj
    for i in range(2, n + 1):
        bi = b0[i]
        bi.sort(reverse=True)
        u = 0
        for j in bi:
            u = max(u, u ^ j)
        d[f(parent[i], i)] = u
    dp = [-1] * (20 * (n + 1))
    for i in range(1, n + 1):
        dp[i] = parent[i]
    for l in range(n + 1, 20 * (n + 1), n + 1):
        f0 = 0
        for i in range(l + 1, l + n + 1):
            if not dp[i - n - 1] == -1:
                f0 = 1
                dp[i] = dp[dp[i - n - 1] + l - n - 1]
        if not f0:
            break
    q = int(input())
    for _ in range(q):
        (r, v) = map(int, input().split())
        (dr, dv) = (dist[r], dist[v])
        if r == v:
            ans0 = d[f(1, -1)]
        elif dr <= dv:
            ans0 = d[f(v, parent[v])]
        else:
            u = dr - dv - 1
            i = 0
            while u:
                if u & pow2[i]:
                    r = dp[f(i, r)]
                    u ^= pow2[i]
                i += 1
            if parent[r] == v:
                ans0 = d[f(v, r)]
            else:
                ans0 = d[f(v, parent[v])]
        ans.append(ans0)
sys.stdout.write('\n'.join(map(str, ans)))

Solution (Python):
import sys, os, io
input = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline

def make_graph(n, m):
    (x, s) = ([], [0] * (n + 3))
    for _ in range(m):
        (u, v) = map(int, input().split())
        s[u + 2] += 1
        s[v + 2] += 1
        x.append(u)
        x.append(v)
    for i in range(3, n + 3):
        s[i] += s[i - 1]
    G = [0] * (2 * m)
    for i in range(2 * m):
        j = x[i] + 1
        G[s[j]] = x[i ^ 1]
        s[j] += 1
    return (G, s)

def bfs(s):
    q = [s]
    dist = [inf] * (n + 1)
    dist[s] = 0
    parent = [-1] * (n + 1)
    child = [[] for _ in range(n + 1)]
    for k in range(n):
        i = q[k]
        di = dist[i]
        for v in range(s0[i], s0[i + 1]):
            j = G[v]
            if dist[j] == inf:
                q.append(j)
                dist[j] = di + 1
                parent[j] = i
                child[i].append(j)
    return (q, parent, child, dist)

def f(u, v):
    return u * (n + 1) + v
t = int(input())
ans = []
inf = pow(10, 9) + 1
pow2 = [1]
for _ in range(20):
    pow2.append(2 * pow2[-1])
for _ in range(t):
    n = int(input())
    a = [0] + list(map(int, input().split()))
    (G, s0) = make_graph(n, n - 1)
    (p, parent, child, dist) = bfs(1)
    b = [[] for _ in range(n + 1)]
    b[1].append(a[1])
    for i in p[::-1][:-1]:
        u = a[i]
        bi = b[i]
        for v in bi:
            u = min(u, u ^ v)
        if u:
            bi.append(u)
        bj = b[parent[i]]
        for u0 in bi:
            u = u0
            for v in bj:
                u = min(u, u ^ v)
            if u:
                bj.append(u)
    d = dict()
    for i in range(1, n + 1):
        bi = b[i]
        bi.sort(reverse=True)
        u = 0
        for j in bi:
            u = max(u, u ^ j)
        d[f(i, parent[i])] = u
    b0 = [[] for _ in range(n + 1)]
    for i in p:
        ci = child[i]
        if not ci:
            continue
        (l, l0) = ([[]], [])
        for j in ci:
            bj = b[j]
            for u0 in bj:
                u = u0
                for v in l0:
                    u = min(u, u ^ v)
                if u:
                    l0.append(u)
            l.append(list(l0))
        (r, r0) = ([[]], [])
        for j in ci[::-1]:
            bj = b[j]
            for u0 in bj:
                u = u0
                for v in r0:
                    u = min(u, u ^ v)
                if u:
                    r0.append(u)
            r.append(list(r0))
        r.reverse()
        for k in range(len(ci)):
            j = ci[k]
            bj = list(b0[i])
            for w in [l[k], r[k + 1], [a[i]]]:
                for u0 in w:
                    u = u0
                    for v in bj:
                        u = min(u, u ^ v)
                    if u:
                        bj.append(u)
            b0[j] = bj
    for i in range(2, n + 1):
        bi = b0[i]
        bi.sort(reverse=True)
        u = 0
        for j in bi:
            u = max(u, u ^ j)
        d[f(parent[i], i)] = u
    dp = [-1] * (20 * (n + 1))
    for i in range(1, n + 1):
        dp[i] = parent[i]
    for l in range(n + 1, 20 * (n + 1), n + 1):
        f0 = 0
        for i in range(l + 1, l + n + 1):
            if not dp[i - n - 1] == -1:
                f0 = 1
                dp[i] = dp[dp[i - n - 1] + l - n - 1]
        if not f0:
            break
    q = int(input())
    for _ in range(q):
        (r, v) = map(int, input().split())
        (dr, dv) = (dist[r], dist[v])
        if r == v:
            ans0 = d[f(1, -1)]
        elif dr <= dv:
            ans0 = d[f(v, parent[v])]
        else:
            u = dr - dv - 1
            i = 0
            while u:
                if u & pow2[i]:
                    r = dp[f(i, r)]
                    u ^= pow2[i]
                i += 1
            if parent[r] == v:
                ans0 = d[f(v, r)]
            else:
                ans0 = d[f(v, parent[v])]
        ans.append(ans0)
sys.stdout.write('\n'.join(map(str, ans)))

Solution (Python):
import sys, os, io
input = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline

def make_graph(n, m):
    (x, s) = ([], [0] * (n + 3))
    for _ in range(m):
        (u, v) = map(int, input().split())
        s[u + 2] += 1
        s[v + 2] += 1
        x.append(u)
        x.append(v)
    for i in range(3, n + 3):
        s[i] += s[i - 1]
    G = [0] * (2 * m)
    for i in range(2 * m):
        j = x[i] + 1
        G[s[j]] = x[i ^ 1]
        s[j] += 1
    return (G, s)

def bfs(s):
    q = [s]
    dist = [inf] * (n + 1)
    dist[s] = 0
    parent = [-1] * (n + 1)
    for k in range(n):
        i = q[k]
        di = dist[i]
        for v in range(s0[i], s0[i + 1]):
            j = G[v]
            if dist[j] == inf:
                q.append(j)
                dist[j] = di + 1
                parent[j] = i
    return (q, parent, dist)

def f(u, v):
    return u * (n + 1) + v

def g(u, v):
    return 31 * u + v
t = int(input())
ans = []
inf = pow(10, 9) + 1
pow2 = [1]
for _ in range(20):
    pow2.append(2 * pow2[-1])
for _ in range(t):
    n = int(input())
    a = [0] + list(map(int, input().split()))
    (G, s0) = make_graph(n, n - 1)
    (p, parent, dist) = bfs(1)
    child = [0] * (n - 1)
    s1 = [0] * (n + 2)
    k = 0
    for i in range(1, n + 1):
        di = dist[i]
        for v in range(s0[i], s0[i + 1]):
            j = G[v]
            if dist[j] > di:
                child[k] = j
                k += 1
        s1[i + 1] = k
    b1 = [0] * (31 * (n + 1))
    l1 = [0] * (n + 1)
    (b1[31], l1[1]) = (a[1], 1)
    for i in p[::-1][:-1]:
        u = a[i]
        bi = b1[i]
        for v in range(31 * i, 31 * i + l1[i]):
            u = min(u, u ^ b1[v])
        if u:
            b1[g(i, l1[i])] = u
            l1[i] += 1
        j = parent[i]
        for k in range(31 * i, 31 * i + l1[i]):
            u = b1[k]
            for v in range(31 * j, 31 * j + l1[j]):
                u = min(u, u ^ b1[v])
            if u:
                b1[g(j, l1[j])] = u
                l1[j] += 1
    d = dict()
    for i in range(1, n + 1):
        bi = [b1[j] for j in range(31 * i, 31 * i + l1[i])]
        bi.sort(reverse=True)
        u = 0
        for j in bi:
            u = max(u, u ^ j)
        d[f(i, parent[i])] = u
    b2 = [0] * (31 * (n + 1))
    l2 = [0] * (n + 1)
    for i in p:
        ci = [child[j] for j in range(s1[i], s1[i + 1])]
        if not ci:
            continue
        (l, l0) = ([[]], [])
        for j in ci:
            for k in range(31 * j, 31 * j + l1[j]):
                u = b1[k]
                for v in l0:
                    u = min(u, u ^ v)
                if u:
                    l0.append(u)
            l.append(list(l0))
        (r, r0) = ([[]], [])
        for j in ci[::-1]:
            for k in range(31 * j, 31 * j + l1[j]):
                u = b1[k]
                for v in r0:
                    u = min(u, u ^ v)
                if u:
                    r0.append(u)
            r.append(list(r0))
        r.reverse()
        for k in range(len(ci)):
            j = ci[k]
            (u, v) = (31 * i, 31 * j)
            for w in range(l2[i]):
                b2[v + w] = b2[u + w]
            l2[j] = l2[i]
            for w in [l[k], r[k + 1], [a[i]]]:
                for u0 in w:
                    u = u0
                    for v in range(31 * j, 31 * j + l2[j]):
                        u = min(u, u ^ b2[v])
                    if u:
                        b2[g(j, l2[j])] = u
                        l2[j] += 1
    for i in range(2, n + 1):
        bi = [b2[j] for j in range(31 * i, 31 * i + l2[i])]
        bi.sort(reverse=True)
        u = 0
        for j in bi:
            u = max(u, u ^ j)
        d[f(parent[i], i)] = u
    dp = [-1] * (20 * (n + 1))
    for i in range(1, n + 1):
        dp[i] = parent[i]
    for l in range(n + 1, 20 * (n + 1), n + 1):
        f0 = 0
        for i in range(l + 1, l + n + 1):
            if not dp[i - n - 1] == -1:
                f0 = 1
                dp[i] = dp[dp[i - n - 1] + l - n - 1]
        if not f0:
            break
    q = int(input())
    for _ in range(q):
        (r, v) = map(int, input().split())
        (dr, dv) = (dist[r], dist[v])
        if r == v:
            ans0 = d[f(1, -1)]
        elif dr <= dv:
            ans0 = d[f(v, parent[v])]
        else:
            u = dr - dv - 1
            i = 0
            while u:
                if u & pow2[i]:
                    r = dp[f(i, r)]
                    u ^= pow2[i]
                i += 1
            if parent[r] == v:
                ans0 = d[f(v, r)]
            else:
                ans0 = d[f(v, parent[v])]
        ans.append(ans0)
sys.stdout.write('\n'.join(map(str, ans)))