D - 共通巡回記録 / Common Tour Record 解説 by admin
gpt-5.3-codexOverview
For each tree, we enumerate all possible traversal records (a/b strings) that can be created by freely changing the order in which children are visited, and finally output the intersection of the two sets in lexicographic order.
Since the constraint \(N \le 10\) is small, recursively constructing the set of possible strings for each subtree is fast enough.
Analysis
The traversal record at each vertex \(v\) always has the form:
awhen entering- Process children in order
bwhen leaving
In other words, the record for vertex \(v\) is always:
\[ \text{"a"} + (\text{concatenation of child subtree records in order}) + \text{"b"} \]
Key Observation
- Since only the order of children is a degree of freedom, the candidate strings for vertex \(v\) can be constructed by combining “the candidate set for each child” × “permutations of children.”
- For a leaf, there is only one candidate:
ab. - The candidates for the entire tree are the candidate set of the root (room 1).
Issues with the Naive Approach
Directly trying all DFS orderings for the entire tree involves permutation branching at every vertex, making implementation complex.
Additionally, the same string can be generated multiple times (e.g., from isomorphic subtrees), so deduplication is necessary.
Solution
- First, root the tree at vertex 1 and build the children arrays.
dfs(v)returns “the set of all possible traversal records from the subtree rooted at vertex \(v\).”- Enumerate all permutations of children using
next_permutation, and for each permutation, concatenate via Cartesian product. - Insert into a
set<string>for deduplication. - Perform this separately for Takahashi’s tree and Aoki’s tree, then take the set intersection at the end.
Algorithm
- Traverse the input undirected tree from root 1 using BFS, constructing
parentandchildren. - Recursive function
dfs(v):- For each child
c, obtainchildSets[c] = dfs(c). - If there are 0 children, return
{"ab"}. - Iterate over all permutations of the child index array
idx = [0,1,...,k-1]. - For each permutation,
starting withcur = {""}, successively computecur = { pre + add | pre in cur, add in childSets[id] }to build concatenation candidates. - For each
mid in cur, add"a" + mid + "b"to theset. - Return the contents of the
setas avector<string>.
- For each child
- Obtain the root candidate sets
SAandSBfor each of the two trees. - After sorting and deduplicating
SAandSB, compute the intersectioninterusingset_intersection. - Output the count and the strings in order.
Complexity
- Time complexity: Strictly depends on the total number of generated strings.
At each vertex, we enumerate “\(k!\) child permutations” and “the Cartesian product of child sets,” so
the overall complexity is roughly proportional to the output size (and intermediate generation size), which is exponential.
However, since \(N \le 10\), this is sufficiently feasible. - Space complexity: Depends on the total size of generated and stored string sets (also dependent on output size).
Implementation Notes
Converting to a rooted tree (assigning parent-child directions) first makes the recursion easier to write.
Since the same string can be generated through multiple paths, use
set<string>at each vertex for deduplication.Sorting
SAandSBfor the final intersection allows the use ofset_intersection, keeping the code concise.The string length is always \(2N\), so comparisons (lexicographic order) are easy to handle.
Source Code
#include <bits/stdc++.h>
using namespace std;
static vector<string> enumerate_records(int N, const vector<vector<int>>& g) {
vector<int> parent(N + 1, -1);
vector<vector<int>> children(N + 1);
// root at 1
{
queue<int> q;
q.push(1);
parent[1] = 0;
while (!q.empty()) {
int v = q.front(); q.pop();
for (int to : g[v]) {
if (parent[to] != -1) continue;
parent[to] = v;
children[v].push_back(to);
q.push(to);
}
}
}
function<vector<string>(int)> dfs = [&](int v) -> vector<string> {
vector<vector<string>> childSets;
childSets.reserve(children[v].size());
for (int c : children[v]) childSets.push_back(dfs(c));
int k = (int)children[v].size();
if (k == 0) return vector<string>{"ab"};
vector<int> idx(k);
iota(idx.begin(), idx.end(), 0);
set<string> uniq; // deduplicate due to identical child-subtrees' strings
do {
vector<string> cur{""};
for (int id : idx) {
vector<string> nxt;
nxt.reserve(cur.size() * childSets[id].size());
for (const string& pre : cur) {
for (const string& add : childSets[id]) {
nxt.push_back(pre + add);
}
}
cur.swap(nxt);
}
for (auto &mid : cur) {
uniq.insert("a" + mid + "b");
}
} while (next_permutation(idx.begin(), idx.end()));
vector<string> res(uniq.begin(), uniq.end());
return res;
};
return dfs(1);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N;
cin >> N;
vector<vector<int>> gA(N + 1), gB(N + 1);
for (int i = 0; i < N - 1; i++) {
int u, v;
cin >> u >> v;
gA[u].push_back(v);
gA[v].push_back(u);
}
for (int i = 0; i < N - 1; i++) {
int u, v;
cin >> u >> v;
gB[u].push_back(v);
gB[v].push_back(u);
}
vector<string> SA = enumerate_records(N, gA);
vector<string> SB = enumerate_records(N, gB);
vector<string> inter;
{
sort(SA.begin(), SA.end());
SA.erase(unique(SA.begin(), SA.end()), SA.end());
sort(SB.begin(), SB.end());
SB.erase(unique(SB.begin(), SB.end()), SB.end());
set_intersection(SA.begin(), SA.end(), SB.begin(), SB.end(), back_inserter(inter));
}
cout << inter.size() << '\n';
for (auto &s : inter) cout << s << '\n';
return 0;
}
This editorial was generated by gpt-5.3-codex.
投稿日時:
最終更新: