import sys
from collections import defaultdict
sys.setrecursionlimit(10**7)
class SegmentTree:
def __init__(self, data, k):
self.n = len(data)
self.k = k
self.tree = [0] * (4 * self.n)
self.build(data, 1, 0, self.n-1)
def build(self, data, node, start, end):
if start == end:
self.tree[node] = data[start]
else:
mid = (start + end) // 2
self.build(data, 2*node, start, mid)
self.build(data, 2*node+1, mid+1, end)
self.tree[node] = self.tree[2*node] & self.tree[2*node+1]
def query(self, node, start, end, l, r):
if r < start or end < l:
return (1 << self.k) - 1
if l <= start and end <= r:
return self.tree[node]
mid = (start + end) // 2
left = self.query(2*node, start, mid, l, r)
right = self.query(2*node+1, mid+1, end, l, r)
return left & right
class HLD:
def __init__(self, n, k, adj, color_sets):
self.n = n
self.k = k
self.adj = adj
self.color_sets = color_sets
self.size = [0] * (n+1)
self.depth = [0] * (n+1)
self.parent = [0] * (n+1)
self.heavy = [-1] * (n+1)
self.head = [0] * (n+1)
self.pos = [0] * (n+1)
self.curr_pos = 0
self.dfs(1, 0)
self.decompose(1, 1)
data = [0] * n
for u in range(1, n+1):
bitset = 0
for c in self.color_sets[u]:
bitset |= 1 << (c - 1)
data[self.pos[u]] = bitset
self.segtree = SegmentTree(data, k)
def dfs(self, u, p):
self.size[u] = 1
self.parent[u] = p
max_size = 0
for v in self.adj[u]:
if v == p:
continue
self.depth[v] = self.depth[u] + 1
self.dfs(v, u)
self.size[u] += self.size[v]
if self.size[v] > max_size:
max_size = self.size[v]
self.heavy[u] = v
def decompose(self, u, h):
self.head[u] = h
self.pos[u] = self.curr_pos
self.curr_pos += 1
if self.heavy[u] != -1:
self.decompose(self.heavy[u], h)
for v in self.adj[u]:
if v != self.parent[u] and v != self.heavy[u]:
self.decompose(v, v)
def query_path(self, u, v):
res = (1 << self.k) - 1
while self.head[u] != self.head[v]:
if self.depth[self.head[u]] < self.depth[self.head[v]]:
u, v = v, u
start = self.pos[self.head[u]]
end = self.pos[u]
res &= self.segtree.query(1, 0, self.n - 1, start, end)
u = self.parent[self.head[u]]
if self.depth[u] > self.depth[v]:
u, v = v, u
res &= self.segtree.query(1, 0, self.n - 1, self.pos[u], self.pos[v])
return bin(res).count('1')
def main():
t = int(input())
output = []
for _ in range(t):
n, k, s, q = map(int, input().split())
adj = defaultdict(list)
for __ in range(n-1):
u, v = map(int, input().split())
adj[u].append(v)
adj[v].append(u)
color_sets = {i: set() for i in range(1, n+1)}
for __ in range(s):
v, x = map(int, input().split())
color_sets[v].add(x)
queries = []
for __ in range(q):
u, v = map(int, input().split())
queries.append((u, v))
hld = HLD(n, k, adj, color_sets)
results = [str(hld.query_path(u, v)) for u, v in queries]
output.append(' '.join(results))
print('\n'.join(output))
main()
aW1wb3J0IHN5cwpmcm9tIGNvbGxlY3Rpb25zIGltcG9ydCBkZWZhdWx0ZGljdApzeXMuc2V0cmVjdXJzaW9ubGltaXQoMTAqKjcpCgpjbGFzcyBTZWdtZW50VHJlZToKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBkYXRhLCBrKToKICAgICAgICBzZWxmLm4gPSBsZW4oZGF0YSkKICAgICAgICBzZWxmLmsgPSBrCiAgICAgICAgc2VsZi50cmVlID0gWzBdICogKDQgKiBzZWxmLm4pCiAgICAgICAgc2VsZi5idWlsZChkYXRhLCAxLCAwLCBzZWxmLm4tMSkKCiAgICBkZWYgYnVpbGQoc2VsZiwgZGF0YSwgbm9kZSwgc3RhcnQsIGVuZCk6CiAgICAgICAgaWYgc3RhcnQgPT0gZW5kOgogICAgICAgICAgICBzZWxmLnRyZWVbbm9kZV0gPSBkYXRhW3N0YXJ0XQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIG1pZCA9IChzdGFydCArIGVuZCkgLy8gMgogICAgICAgICAgICBzZWxmLmJ1aWxkKGRhdGEsIDIqbm9kZSwgc3RhcnQsIG1pZCkKICAgICAgICAgICAgc2VsZi5idWlsZChkYXRhLCAyKm5vZGUrMSwgbWlkKzEsIGVuZCkKICAgICAgICAgICAgc2VsZi50cmVlW25vZGVdID0gc2VsZi50cmVlWzIqbm9kZV0gJiBzZWxmLnRyZWVbMipub2RlKzFdCgogICAgZGVmIHF1ZXJ5KHNlbGYsIG5vZGUsIHN0YXJ0LCBlbmQsIGwsIHIpOgogICAgICAgIGlmIHIgPCBzdGFydCBvciBlbmQgPCBsOgogICAgICAgICAgICByZXR1cm4gKDEgPDwgc2VsZi5rKSAtIDEKICAgICAgICBpZiBsIDw9IHN0YXJ0IGFuZCBlbmQgPD0gcjoKICAgICAgICAgICAgcmV0dXJuIHNlbGYudHJlZVtub2RlXQogICAgICAgIG1pZCA9IChzdGFydCArIGVuZCkgLy8gMgogICAgICAgIGxlZnQgPSBzZWxmLnF1ZXJ5KDIqbm9kZSwgc3RhcnQsIG1pZCwgbCwgcikKICAgICAgICByaWdodCA9IHNlbGYucXVlcnkoMipub2RlKzEsIG1pZCsxLCBlbmQsIGwsIHIpCiAgICAgICAgcmV0dXJuIGxlZnQgJiByaWdodAoKY2xhc3MgSExEOgogICAgZGVmIF9faW5pdF9fKHNlbGYsIG4sIGssIGFkaiwgY29sb3Jfc2V0cyk6CiAgICAgICAgc2VsZi5uID0gbgogICAgICAgIHNlbGYuayA9IGsKICAgICAgICBzZWxmLmFkaiA9IGFkagogICAgICAgIHNlbGYuY29sb3Jfc2V0cyA9IGNvbG9yX3NldHMKICAgICAgICBzZWxmLnNpemUgPSBbMF0gKiAobisxKQogICAgICAgIHNlbGYuZGVwdGggPSBbMF0gKiAobisxKQogICAgICAgIHNlbGYucGFyZW50ID0gWzBdICogKG4rMSkKICAgICAgICBzZWxmLmhlYXZ5ID0gWy0xXSAqIChuKzEpCiAgICAgICAgc2VsZi5oZWFkID0gWzBdICogKG4rMSkKICAgICAgICBzZWxmLnBvcyA9IFswXSAqIChuKzEpCiAgICAgICAgc2VsZi5jdXJyX3BvcyA9IDAKCiAgICAgICAgc2VsZi5kZnMoMSwgMCkKICAgICAgICBzZWxmLmRlY29tcG9zZSgxLCAxKQoKICAgICAgICBkYXRhID0gWzBdICogbgogICAgICAgIGZvciB1IGluIHJhbmdlKDEsIG4rMSk6CiAgICAgICAgICAgIGJpdHNldCA9IDAKICAgICAgICAgICAgZm9yIGMgaW4gc2VsZi5jb2xvcl9zZXRzW3VdOgogICAgICAgICAgICAgICAgYml0c2V0IHw9IDEgPDwgKGMgLSAxKQogICAgICAgICAgICBkYXRhW3NlbGYucG9zW3VdXSA9IGJpdHNldAoKICAgICAgICBzZWxmLnNlZ3RyZWUgPSBTZWdtZW50VHJlZShkYXRhLCBrKQoKICAgIGRlZiBkZnMoc2VsZiwgdSwgcCk6CiAgICAgICAgc2VsZi5zaXplW3VdID0gMQogICAgICAgIHNlbGYucGFyZW50W3VdID0gcAogICAgICAgIG1heF9zaXplID0gMAogICAgICAgIGZvciB2IGluIHNlbGYuYWRqW3VdOgogICAgICAgICAgICBpZiB2ID09IHA6CiAgICAgICAgICAgICAgICBjb250aW51ZQogICAgICAgICAgICBzZWxmLmRlcHRoW3ZdID0gc2VsZi5kZXB0aFt1XSArIDEKICAgICAgICAgICAgc2VsZi5kZnModiwgdSkKICAgICAgICAgICAgc2VsZi5zaXplW3VdICs9IHNlbGYuc2l6ZVt2XQogICAgICAgICAgICBpZiBzZWxmLnNpemVbdl0gPiBtYXhfc2l6ZToKICAgICAgICAgICAgICAgIG1heF9zaXplID0gc2VsZi5zaXplW3ZdCiAgICAgICAgICAgICAgICBzZWxmLmhlYXZ5W3VdID0gdgoKICAgIGRlZiBkZWNvbXBvc2Uoc2VsZiwgdSwgaCk6CiAgICAgICAgc2VsZi5oZWFkW3VdID0gaAogICAgICAgIHNlbGYucG9zW3VdID0gc2VsZi5jdXJyX3BvcwogICAgICAgIHNlbGYuY3Vycl9wb3MgKz0gMQogICAgICAgIGlmIHNlbGYuaGVhdnlbdV0gIT0gLTE6CiAgICAgICAgICAgIHNlbGYuZGVjb21wb3NlKHNlbGYuaGVhdnlbdV0sIGgpCiAgICAgICAgZm9yIHYgaW4gc2VsZi5hZGpbdV06CiAgICAgICAgICAgIGlmIHYgIT0gc2VsZi5wYXJlbnRbdV0gYW5kIHYgIT0gc2VsZi5oZWF2eVt1XToKICAgICAgICAgICAgICAgIHNlbGYuZGVjb21wb3NlKHYsIHYpCgogICAgZGVmIHF1ZXJ5X3BhdGgoc2VsZiwgdSwgdik6CiAgICAgICAgcmVzID0gKDEgPDwgc2VsZi5rKSAtIDEKICAgICAgICB3aGlsZSBzZWxmLmhlYWRbdV0gIT0gc2VsZi5oZWFkW3ZdOgogICAgICAgICAgICBpZiBzZWxmLmRlcHRoW3NlbGYuaGVhZFt1XV0gPCBzZWxmLmRlcHRoW3NlbGYuaGVhZFt2XV06CiAgICAgICAgICAgICAgICB1LCB2ID0gdiwgdQogICAgICAgICAgICBzdGFydCA9IHNlbGYucG9zW3NlbGYuaGVhZFt1XV0KICAgICAgICAgICAgZW5kID0gc2VsZi5wb3NbdV0KICAgICAgICAgICAgcmVzICY9IHNlbGYuc2VndHJlZS5xdWVyeSgxLCAwLCBzZWxmLm4gLSAxLCBzdGFydCwgZW5kKQogICAgICAgICAgICB1ID0gc2VsZi5wYXJlbnRbc2VsZi5oZWFkW3VdXQogICAgICAgIGlmIHNlbGYuZGVwdGhbdV0gPiBzZWxmLmRlcHRoW3ZdOgogICAgICAgICAgICB1LCB2ID0gdiwgdQogICAgICAgIHJlcyAmPSBzZWxmLnNlZ3RyZWUucXVlcnkoMSwgMCwgc2VsZi5uIC0gMSwgc2VsZi5wb3NbdV0sIHNlbGYucG9zW3ZdKQogICAgICAgIHJldHVybiBiaW4ocmVzKS5jb3VudCgnMScpCgpkZWYgbWFpbigpOgogICAgdCA9IGludChpbnB1dCgpKQogICAgb3V0cHV0ID0gW10KICAgIGZvciBfIGluIHJhbmdlKHQpOgogICAgICAgIG4sIGssIHMsIHEgPSBtYXAoaW50LCBpbnB1dCgpLnNwbGl0KCkpCiAgICAgICAgYWRqID0gZGVmYXVsdGRpY3QobGlzdCkKICAgICAgICBmb3IgX18gaW4gcmFuZ2Uobi0xKToKICAgICAgICAgICAgdSwgdiA9IG1hcChpbnQsIGlucHV0KCkuc3BsaXQoKSkKICAgICAgICAgICAgYWRqW3VdLmFwcGVuZCh2KQogICAgICAgICAgICBhZGpbdl0uYXBwZW5kKHUpCgogICAgICAgIGNvbG9yX3NldHMgPSB7aTogc2V0KCkgZm9yIGkgaW4gcmFuZ2UoMSwgbisxKX0KICAgICAgICBmb3IgX18gaW4gcmFuZ2Uocyk6CiAgICAgICAgICAgIHYsIHggPSBtYXAoaW50LCBpbnB1dCgpLnNwbGl0KCkpCiAgICAgICAgICAgIGNvbG9yX3NldHNbdl0uYWRkKHgpCgogICAgICAgIHF1ZXJpZXMgPSBbXQogICAgICAgIGZvciBfXyBpbiByYW5nZShxKToKICAgICAgICAgICAgdSwgdiA9IG1hcChpbnQsIGlucHV0KCkuc3BsaXQoKSkKICAgICAgICAgICAgcXVlcmllcy5hcHBlbmQoKHUsIHYpKQoKICAgICAgICBobGQgPSBITEQobiwgaywgYWRqLCBjb2xvcl9zZXRzKQogICAgICAgIHJlc3VsdHMgPSBbc3RyKGhsZC5xdWVyeV9wYXRoKHUsIHYpKSBmb3IgdSwgdiBpbiBxdWVyaWVzXQogICAgICAgIG91dHB1dC5hcHBlbmQoJyAnLmpvaW4ocmVzdWx0cykpCgogICAgcHJpbnQoJ1xuJy5qb2luKG91dHB1dCkpCgptYWluKCkK