#line 2 "/Users/noya2/Desktop/Noya2_library/template/template.hpp" using namespace std; #include #line 1 "/Users/noya2/Desktop/Noya2_library/template/inout_old.hpp" namespace noya2 { template ostream &operator<<(ostream &os, const pair &p){ os << p.first << " " << p.second; return os; } template istream &operator>>(istream &is, pair &p){ is >> p.first >> p.second; return is; } template ostream &operator<<(ostream &os, const vector &v){ int s = (int)v.size(); for (int i = 0; i < s; i++) os << (i ? " " : "") << v[i]; return os; } template istream &operator>>(istream &is, vector &v){ for (auto &x : v) is >> x; return is; } void in() {} template void in(T &t, U &...u){ cin >> t; in(u...); } void out() { cout << "\n"; } template void out(const T &t, const U &...u){ cout << t; if (sizeof...(u)) cout << sep; out(u...); } template void out(const vector> &vv){ int s = (int)vv.size(); for (int i = 0; i < s; i++) out(vv[i]); } struct IoSetup { IoSetup(){ cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(15); cerr << fixed << setprecision(7); } } iosetup_noya2; } // namespace noya2 #line 1 "/Users/noya2/Desktop/Noya2_library/template/const.hpp" namespace noya2{ const int iinf = 1'000'000'007; const long long linf = 2'000'000'000'000'000'000LL; const long long mod998 = 998244353; const long long mod107 = 1000000007; const long double pi = 3.14159265358979323; const vector dx = {0,1,0,-1,1,1,-1,-1}; const vector dy = {1,0,-1,0,1,-1,-1,1}; const string ALP = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; const string alp = "abcdefghijklmnopqrstuvwxyz"; const string NUM = "0123456789"; void yes(){ cout << "Yes\n"; } void no(){ cout << "No\n"; } void YES(){ cout << "YES\n"; } void NO(){ cout << "NO\n"; } void yn(bool t){ t ? yes() : no(); } void YN(bool t){ t ? YES() : NO(); } } // namespace noya2 #line 2 "/Users/noya2/Desktop/Noya2_library/template/utils.hpp" #line 6 "/Users/noya2/Desktop/Noya2_library/template/utils.hpp" namespace noya2{ unsigned long long inner_binary_gcd(unsigned long long a, unsigned long long b){ if (a == 0 || b == 0) return a + b; int n = __builtin_ctzll(a); a >>= n; int m = __builtin_ctzll(b); b >>= m; while (a != b) { int mm = __builtin_ctzll(a - b); bool f = a > b; unsigned long long c = f ? a : b; b = f ? b : a; a = (c - b) >> mm; } return a << std::min(n, m); } template T gcd_fast(T a, T b){ return static_cast(inner_binary_gcd(std::abs(a),std::abs(b))); } long long sqrt_fast(long long n) { if (n <= 0) return 0; long long x = sqrt(n); while ((x + 1) * (x + 1) <= n) x++; while (x * x > n) x--; return x; } template T floor_div(const T n, const T d) { assert(d != 0); return n / d - static_cast((n ^ d) < 0 && n % d != 0); } template T ceil_div(const T n, const T d) { assert(d != 0); return n / d + static_cast((n ^ d) >= 0 && n % d != 0); } template void uniq(std::vector &v){ std::sort(v.begin(),v.end()); v.erase(unique(v.begin(),v.end()),v.end()); } template inline bool chmin(T &x, U y) { return (y < x) ? (x = y, true) : false; } template inline bool chmax(T &x, U y) { return (x < y) ? (x = y, true) : false; } template inline bool range(T l, T x, T r){ return l <= x && x < r; } } // namespace noya2 #line 8 "/Users/noya2/Desktop/Noya2_library/template/template.hpp" #define rep(i,n) for (int i = 0; i < (int)(n); i++) #define repp(i,m,n) for (int i = (m); i < (int)(n); i++) #define reb(i,n) for (int i = (int)(n-1); i >= 0; i--) #define all(v) (v).begin(),(v).end() using ll = long long; using ld = long double; using uint = unsigned int; using ull = unsigned long long; using pii = pair; using pll = pair; using pil = pair; using pli = pair; namespace noya2{ /* ~ (. _________ . /) */ } using namespace noya2; #line 2 "c.cpp" #line 2 "/Users/noya2/Desktop/Noya2_library/tree/heavy_light_decomposition.hpp" #line 2 "/Users/noya2/Desktop/Noya2_library/tree/simple_tree.hpp" #line 2 "/Users/noya2/Desktop/Noya2_library/data_structure/csr.hpp" #line 4 "/Users/noya2/Desktop/Noya2_library/data_structure/csr.hpp" #include #line 7 "/Users/noya2/Desktop/Noya2_library/data_structure/csr.hpp" namespace noya2::internal { template struct csr { csr () {} csr (int _n) : n(_n) {} csr (int _n, int m) : n(_n){ start.reserve(m); elist.reserve(m); } // ACL style constructor (do not have to call build) csr (int _n, const std::vector> &idx_elem) : n(_n), start(_n + 2), elist(idx_elem.size()) { for (auto &[i, e] : idx_elem){ start[i + 2]++; } for (int i = 1; i < n; i++){ start[i + 2] += start[i + 1]; } for (auto &[i, e] : idx_elem){ elist[start[i + 1]++] = e; } prepared = true; } int add(int idx, E elem){ int eid = start.size(); start.emplace_back(idx); elist.emplace_back(elem); return eid; } void build(){ if (prepared) return ; int m = start.size(); std::vector nelist(m); std::vector nstart(n + 2, 0); for (int i = 0; i < m; i++){ nstart[start[i] + 2]++; } for (int i = 1; i < n; i++){ nstart[i + 2] += nstart[i + 1]; } for (int i = 0; i < m; i++){ nelist[nstart[start[i] + 1]++] = elist[i]; } swap(elist,nelist); swap(start,nstart); prepared = true; } const auto operator[](int idx) const { return std::ranges::subrange(elist.begin()+start[idx],elist.begin()+start[idx+1]); } auto operator[](int idx){ return std::ranges::subrange(elist.begin()+start[idx],elist.begin()+start[idx+1]); } const auto operator()(int idx, int l, int r) const { return std::ranges::subrange(elist.begin()+start[idx]+l,elist.begin()+start[idx]+r); } auto operator()(int idx, int l, int r){ return std::ranges::subrange(elist.begin()+start[idx]+l,elist.begin()+start[idx]+r); } size_t size() const { return n; } int n; std::vector start; std::vector elist; bool prepared = false; }; } // namespace noya2::internal #line 5 "/Users/noya2/Desktop/Noya2_library/tree/simple_tree.hpp" namespace noya2 { struct simple_tree { internal::csr g; simple_tree () {} simple_tree (int _n) : g(_n, (_n - 1)*2) { if (_n == 1){ g.build(); } } void add_edge(int u, int v){ g.add(u, v); int id = g.add(v, u); if (id + 1 == (g.n - 1)*2) g.build(); } void input(int indexed = 1){ for (int i = 0; i < g.n - 1; i++){ int u, v; cin >> u >> v; u -= indexed, v -= indexed; add_edge(u, v); } } void input_parents(int indexed = 1){ for (int i = 0; i < g.n - 1; i++){ int v; cin >> v; v -= indexed; add_edge(i + 1, v); } } const auto operator[](int v) const { return g[v]; } auto operator[](int v){ return g[v]; } size_t size() const { return g.size(); } }; } // namespace noya2 #line 5 "/Users/noya2/Desktop/Noya2_library/tree/heavy_light_decomposition.hpp" namespace noya2 { using namespace std; struct hld_tree { internal::csr g; hld_tree () {} hld_tree (int _n, int _root = 0) : g(_n,(_n - 1)*2), n(_n), root(_root) { if (_n == 1){ build(); } } hld_tree (simple_tree _g, int _root = 0) : g(_g.g), n(_g.g.n), root(_root){ build(); } size_t size() const { return g.n; } int root_v() const { return root; } void add_edge(int u, int v){ g.add(u, v); int id = g.add(v, u); if (id + 1 == (n - 1)*2) build(); } void input(int indexed = 1){ for (int i = 0; i < n - 1; i++){ int u, v; cin >> u >> v; u -= indexed, v -= indexed; add_edge(u, v); } } void input_parents(int indexed = 1){ for (int i = 0; i < n - 1; i++){ int v; cin >> v; v -= indexed; add_edge(i + 1, v); } } int depth(int v) const { return dep[v]; } int parent(int v) const { if (v == root) return -1; return g[v].back(); } int degree(int v) const { return g[v].size(); } int subtree_size(int v) const { return sub[v]; } // if d > dep[v], return -1 int la(int v, int d) const { while (v != -1){ int u = nxt[v]; if (down[v] - d >= down[u]){ v = tour[down[v] - d]; break; } d -= down[v] - down[u] + 1; v = parent(u); } return v; } int lca(int u, int v) const { while (nxt[u] != nxt[v]){ if (down[u] < down[v]) swap(u,v); u = parent(nxt[u]); } return dep[u] < dep[v] ? u : v; } int dist(int u, int v) const { return dep[u] + dep[v] - 2*dep[lca(u,v)]; } // if d > dist(from, to), return -1 int jump(int from, int to, int d) const { int l = lca(from,to); if (d <= dep[from] - dep[l]){ return la(from, d); } d -= dep[from] - dep[l]; if (d <= dep[to] - dep[l]){ return la(to, dep[to] - dep[l] - d); } return -1; } // seg.set(index(v), X[v]); int index(int vertex) const { return down[vertex]; } int index_from_edge(int u, int v) const { return (dep[u] < dep[v] ? down[v] : down[u]); } // X[vertex(i)] = seg.get(i); int vertex(int index) const { return tour[index]; } int subtree_l(int v) const { return down[v]; } int subtree_r(int v) const { return down[v] + sub[v]; } // if r == v, return true bool is_in_subtree(int r, int v) const { return subtree_l(r) <= subtree_l(v) && subtree_l(v) < subtree_r(r); } bool is_in_path(int lv, int mv, int rv) const { return dist(lv,mv) + dist(mv,rv) == dist(lv,rv); } // dist, v1, v2 tuple diameter(){ int v1 = max_element(dep.begin(),dep.end()) - dep.begin(); vector dist_from_v1(n,numeric_limits::max()); queue que; que.push(v1); dist_from_v1[v1] = 0; while (!que.empty()){ int v = que.front(); que.pop(); for (int u : g[v]){ if (dist_from_v1[u] > dist_from_v1[v]+1){ dist_from_v1[u] = dist_from_v1[v]+1; que.push(u); } } } int v2 = max_element(dist_from_v1.begin(),dist_from_v1.end()) - dist_from_v1.begin(); return make_tuple(dist_from_v1[v2],v1,v2); } // vertex array : vector {from, v1, v2, ... , to} vector path(int from, int to){ int l = lca(from,to); const int sizf = dep[from]-dep[l], sizt = dep[to]-dep[l]; vector pf = {from}, pt; pf.reserve(sizf+1); pt.reserve(sizt); for (int i = 0; i < sizf; i++){ from = parent(from); pf.push_back(from); } for (int i = 0; i < sizt; i++){ pt.push_back(to); to = parent(to); } pf.insert(pf.end(),pt.rbegin(),pt.rend()); return pf; } template void path_query(int u, int v, bool vertex, const F &f){ int l = lca(u,v); for (auto [s, t] : ascend(u, l)){ f(t, s + 1); } if (vertex) f(down[l], down[l] + 1); for (auto [s, t] : descend(l, v)){ f(s, t + 1); } } template void path_noncommutative_query(int u, int v, bool vertex, const F &f){ int l = lca(u,v); for (auto [s, t] : ascend(u, l)){ f(s + 1, t); // l > r ok } if (vertex) f(down[l],down[l] + 1); for (auto [s, t] : descend(l, v)){ f(s, t + 1); // l > r ok } } template void subtree_query(int v, bool vertex, const F &f){ f(down[v] + (vertex ? 0 : 1), down[v] + sub[v]); } // adjacent to v const auto operator[](int v) const { return g[v]; } auto operator[](int v){ return g[v]; } // only child const auto operator()(int v) const { return g(v, 0, degree(v) - (v == root ? 0 : 1)); } auto operator()(int v){ return g(v, 0, degree(v) - (v == root ? 0 : 1)); } private: int n, root; vector dep, sub, down, tour, nxt; // v is ancestor of u. // enumerate [closed] intervals of down ( interval [l, r] may hold l > r ). vector> ascend(int u, int v){ vector> res; while (nxt[u] != nxt[v]){ res.emplace_back(down[u], down[nxt[u]]); u = parent(nxt[u]); } if (u != v) res.emplace_back(down[u], down[v]+1); return res; } // u is ancestor of v. // enumerate [closed] intervals of down ( interval [l, r] may hold l > r ). vector> descend(int u, int v){ if (u == v) return {}; if (nxt[u] == nxt[v]){ return {pair(down[u]+1, down[v])}; } vector> res = descend(u, parent(nxt[v])); res.emplace_back(down[nxt[v]], down[v]); return res; } void build(){ g.build(); init_sz(); init_hld(); } /* setup dep, sub if v is not root, g[v].back() is parent of v. if v is not leaf (i.e. v has child), g[v].front() is heavy child of v. */ void init_sz(){ dep.resize(n, 0); sub.resize(n, 1); auto dfs = [&](auto sfs, int v, int f) -> void { for (int &u : g[v]){ // only one chance to take parent as u. if (u == f) swap(g[v].back(), u); // twice means u is the last element of g[v], i.e. parent of v. if (u == f) break; dep[u] = dep[v]+1; sfs(sfs, u, v); sub[v] += sub[u]; if (sub[g[v].front()] < sub[u]){ swap(g[v].front(), u); } } }; dfs(dfs, root, -1); } /* setup down, tour, nxt only heavy child c of v, nxt[c] = nxt[v]. for other child c, nxt[c] = c. */ void init_hld(){ down.resize(n); tour.resize(n); nxt.resize(n); nxt[root] = root; int clock = 0; auto dfs = [&](auto sfs, int v) -> void { down[v] = clock++; tour[down[v]] = v; // in case of no child, nothing to do if ((*this)(v).empty()) return ; // heavy child nxt[(*this)(v).front()] = nxt[v]; sfs(sfs, (*this)(v).front()); // other child for (int u : (*this)(v).next()){ nxt[u] = u; sfs(sfs, u); } }; dfs(dfs, root); } public: struct compressed_tree : public simple_tree { using simple_tree::simple_tree; using simple_tree::operator=; hld_tree &g; compressed_tree (hld_tree &g_, vector vs) : g(g_){ auto comp = [&](int lv, int rv){ return g.index(lv) < g.index(rv); }; sort(vs.begin(),vs.end(),comp); int sz = vs.size(); for (int i = 0; i < sz-1; i++){ vs.emplace_back(g.lca(vs[i],vs[i+1])); } sort(vs.begin(),vs.end(),comp); vs.erase(unique(vs.begin(),vs.end()),vs.end()); sz = vs.size(); (*this) = simple_tree(sz); real_vertex = vs; for (int i = 0; i < sz; i++){ g.virtual_vertex[real_vertex[i]] = i; } stack st; st.push(0); for (int i = 1; i < sz; i++){ while (!g.is_in_subtree(real_vertex[st.top()], real_vertex[i])) st.pop(); (*this).add_edge(st.top(),i); st.push(i); } } vector real_vertex; int real_v(int virtual_v) const { return real_vertex[virtual_v]; } int virtual_v(int real_v) const { return g.virtual_vertex[real_v]; } size_t size() const { return real_vertex.size(); } }; compressed_tree compressed_tree_gen(const vector &vs){ if ((int)virtual_vertex.size() != n) virtual_vertex.resize(n); return compressed_tree(*this, vs); } vector virtual_vertex; }; } // namespace noya2 #line 5 "c.cpp" void solve(){ int n; in(n); hld_tree g; { vector es; int nv = n; rep(i,n-1){ int u, v; in(u,v); u--, v--; int w; in(w); w--; while (w--){ es.emplace_back(u,nv); u = nv++; } es.emplace_back(u,v); } g = hld_tree(nv); for (auto [u, v] : es){ g.add_edge(u,v); // out(u,v); } // out(); } simple_tree t; vector coo; { int h, w; in(h,w); vector a(h); in(a); map mp; int nv = 0; auto id = [&](int x, int y){ if (mp.contains({x,y})) return mp[{x,y}]; coo.emplace_back(x,y); return mp[{x,y}] = nv++; }; vector es; rep(i,h) rep(j,w){ if (a[i][j] == '.') continue; if (i > 0 && a[i-1][j] == '#'){ es.emplace_back(id(i,j),id(i-1,j)); } if (j > 0 && a[i][j-1] == '#'){ es.emplace_back(id(i,j),id(i,j-1)); } } t = simple_tree(nv); for (auto [u, v] : es){ t.add_edge(u,v); // out(u,v); } // out(); } if (g.size() != t.size()){ no(); return ; } // pack g into t auto encode = [&](uint s, uint u, uint v) -> uint { return (s << 18) ^ (u << 9) ^ v; }; unordered_map memo; auto dp = [&](auto sfs, int s, int u, int v) -> bool { uint key = encode(s,u,v); if (memo.contains(key)) return memo[key]; vector cs; for (int c : t[v]){ if (c == u) continue; cs.emplace_back(c); } if (g(s).size() != cs.size()){ return memo[key] = false; } sort(all(cs)); do { bool ok = true; for (int i = 0; auto sc : g(s)){ if (!sfs(sfs,sc,v,cs[i])){ ok = false; break; } i++; } if (ok){ return memo[key] = true; } }while(next_permutation(all(cs))); return memo[key] = false; }; int tsz = t.size(); int iu = -1; rep(u,tsz){ if (dp(dp,0,u,u)){ iu = u; break; } } if (iu == -1){ no(); return ; } yes(); vector ans(n); auto rec = [&](auto sfs, int s, int u, int v) -> void { if (s < n){ ans[s] = coo[v]; } vector cs; for (int c : t[v]){ if (c == u) continue; cs.emplace_back(c); } assert(g(s).size() == cs.size()); sort(all(cs)); do { bool ok = true; for (int i = 0; auto sc : g(s)){ if (!dp(dp,sc,v,cs[i])){ ok = false; break; } i++; } if (ok) break; }while(next_permutation(all(cs))); for (int i = 0; auto sc : g(s)){ sfs(sfs,sc,v,cs[i]); i++; } }; rec(rec,0,iu,iu); for (auto [x, y] : ans){ out(x+1,y+1); } } int main(){ int t = 1; in(t); while (t--) { solve(); } }