#include <bits/stdc++.h>
using namespace std;
/*
============================================================
SMALL-TO-LARGE TEMPLATE
Collected ideas and tricks for ECPC & ACPC
============================================================
Contents:
1) Merging different containers (set, map, vector, priority_queue)
2) DSU on Tree (Sack) with custom add/remove functions
3) Merging Maps to calculate answers for each node
4) Advanced techniques: Subtree queries
5) Examples of common problems (color count, sum, GCD, Mex, ...)
6) Handling offline queries
7) Performance improvements (swap, clear, move)
Everyone uses the idea "merge the small into the large" to get O(n log n) or O(n log^2 n).
============================================================
*/
// ==========================================================
// 1) Basic container merge functions
// ==========================================================
// -------- Merge unordered_map (same key) --------
/**
* PURPOSE:
* Merges two unordered_maps by summing values for same keys.
* After merging, the larger map (a) will contain the combined data,
* and the smaller map (b) will be cleared.
*
* HOW TO USE:
* Call mergeUnorderedMap(a, b) where:
* - a: the primary map (will hold the final merged result)
* - b: the map to merge into a (will be emptied)
*
* TIME COMPLEXITY:
* O(size_of_smaller_map) on average.
*
* NOTES:
* - The function ensures that the larger map is the one kept.
* - Values are added using +=. If you need different logic (e.g., assignment),
* modify the line a[k] += v.
*
* CONSTRAINTS:
* - The keys must support operator[] and iteration.
* - Works with any types that support +=.
*/
template<typename K, typename V>
void mergeUnorderedMap(unordered_map<K,V>& a, unordered_map<K,V>& b) {
if (a.size() < b.size()) a.swap(b);
for (auto &[k, v] : b)
a[k] += v;
b.clear();
}
// -------- Merge map (ordered) --------
/**
* PURPOSE:
* Merges two ordered maps by summing values for same keys.
* After merging, the larger map (a) will contain the combined data,
* and the smaller map (b) will be cleared.
*
* HOW TO USE:
* Call mergeMap(a, b) where:
* - a: the primary map (will hold the final merged result)
* - b: the map to merge into a (will be emptied)
*
* TIME COMPLEXITY:
* O(size_of_smaller_map * log(size_of_larger_map)) because each insertion into
* the larger map costs O(log n).
*
* NOTES:
* - The function ensures that the larger map is the one kept.
* - Values are added using +=. If you need different logic, modify the line.
*
* CONSTRAINTS:
* - Keys must be comparable (operator<) because std::map is ordered.
*/
template<typename K, typename V>
void mergeMap(map<K,V>& a, map<K,V>& b) {
if (a.size() < b.size()) a.swap(b);
for (auto &[k, v] : b)
a[k] += v;
b.clear();
}
// -------- Merge set --------
/**
* PURPOSE:
* Merges two sets into one. The resulting set contains all unique elements
* from both sets. The larger set (a) will hold the final result, and the
* smaller set (b) will be cleared.
*
* HOW TO USE:
* Call mergeSet(a, b) where:
* - a: the primary set (will hold the final merged result)
* - b: the set to merge into a (will be emptied)
*
* TIME COMPLEXITY:
* O(size_of_smaller_set * log(size_of_larger_set)).
*
* NOTES:
* - The function ensures that the larger set is the one kept.
* - Duplicates are automatically handled because std::set only stores unique
* elements.
*
* CONSTRAINTS:
* - Elements must be comparable (operator<) because std::set is ordered.
*/
template<typename T>
void mergeSet(set<T>& a, set<T>& b) {
if (a.size() < b.size()) a.swap(b);
for (auto &x : b) a.insert(x);
b.clear();
}
// -------- Merge vector --------
/**
* PURPOSE:
* Merges two vectors by appending all elements of b to a.
* After merging, the larger vector (a) will contain all elements from both,
* and the smaller vector (b) will be cleared.
*
* HOW TO USE:
* Call mergeVector(a, b) where:
* - a: the primary vector (will hold the final merged result)
* - b: the vector to merge into a (will be emptied)
*
* TIME COMPLEXITY:
* O(size_of_smaller_vector) for the insertion.
*
* NOTES:
* - The function ensures that the larger vector is the one kept.
* - Order is preserved: elements from a come first, then elements from b.
* - No deduplication is performed; duplicates remain.
*
* CONSTRAINTS:
* - Works with any type.
*/
template<typename T>
void mergeVector(vector<T>& a, vector<T>& b) {
if (a.size() < b.size()) a.swap(b);
a.insert(a.end(), b.begin(), b.end());
b.clear();
}
// -------- Merge priority_queue --------
/**
* PURPOSE:
* Merges two priority_queues by transferring all elements from b to a.
* After merging, a will contain all elements from both queues,
* and b will be empty.
*
* HOW TO USE:
* Call mergePriorityQueue(a, b) where:
* - a: the primary priority_queue (will hold the final merged result)
* - b: the priority_queue to merge into a (will be emptied)
*
* TIME COMPLEXITY:
* O(size_of_smaller_queue * log(size_of_larger_queue)).
*
* NOTES:
* - The function does NOT automatically swap to keep the larger one because
* priority_queue does not support size-based swapping efficiently.
* - This method is less efficient than using a vector as the underlying
* container. Consider using a vector and building a heap instead.
*
* CONSTRAINTS:
* - Works with any type that priority_queue supports.
*/
template<typename T>
void mergePriorityQueue(priority_queue<T>& a, priority_queue<T>& b) {
while (!b.empty()) {
a.push(b.top());
b.pop();
}
}
// ==========================================================
// 2) DSU on Tree (Sack) – General model with add/remove functions
// ==========================================================
/**
* PURPOSE:
* This structure solves subtree queries on a tree using the DSU on Tree
* (also called "Sack") technique. It computes an answer for each node based
* on the properties of its entire subtree.
*
* Examples of what can be computed:
* - Number of distinct colors in each subtree
* - Sum of values in each subtree
* - GCD, Mex, mode, etc.
*
* HOW TO USE:
* 1. Create an instance: DSUonTree solver(n); where n is the number of nodes.
* 2. Add edges using solver.addEdge(u, v) (0-indexed nodes).
* 3. Customize the addNode() function to update your data when a node is
* added or removed.
* 4. Call solver.solve().
* 5. Results are stored in solver.ans[u] for each node u.
*
* TIME COMPLEXITY:
* O(n log n) for the DFS, plus the cost of addNode/removeNode for each node
* (which is O(1) in most cases). If addNode uses a BIT or map, complexity
* increases accordingly.
*
* NOTES:
* - "Heavy child" means the child with the largest subtree size.
* - "Keep" flag: if true, the subtree's data is kept after the DFS; if false,
* it is removed to free memory.
* - The provided addNode example tracks distinct colors. You must adapt it
* to your specific problem.
*
* CONSTRAINTS:
* - n must be at least 1.
* - Node IDs must be from 0 to n-1.
* - The graph must be a tree (connected, acyclic).
*/
struct DSUonTree {
int n;
vector<vector<int>> adj;
vector<int> sz, heavy;
vector<int> ans;
// Example data: frequency of colors
vector<int> freq;
int distinct = 0;
DSUonTree(int n) : n(n), adj(n), sz(n, 1), heavy(n, -1), ans(n), freq(n+1, 0) {}
void addEdge(int u, int v) {
adj[u].push_back(v);
adj[v].push_back(u);
}
// Computes subtree sizes and identifies the heavy child for each node.
void dfsSize(int u, int p) {
sz[u] = 1;
int mx = 0;
for (int v : adj[u]) {
if (v == p) continue;
dfsSize(v, u);
sz[u] += sz[v];
if (sz[v] > mx) {
mx = sz[v];
heavy[u] = v;
}
}
}
// Add or remove a single node and all its descendants from the data.
// delta = +1 to add, -1 to remove.
void addNode(int u, int p, int delta) {
int color = u; // Example: color = node ID. Change as needed.
if (delta == 1) {
if (freq[color] == 0) distinct++;
freq[color]++;
} else {
freq[color]--;
if (freq[color] == 0) distinct--;
}
for (int v : adj[u]) {
if (v == p) continue;
addNode(v, u, delta);
}
}
void addSubtree(int u, int p, int delta) {
addNode(u, p, delta);
}
// Main DSU on Tree DFS.
// keep = true means we keep the data after finishing this subtree.
void dfs(int u, int p, bool keep) {
// Solve light children and clear their data.
for (int v : adj[u]) {
if (v == p || v == heavy[u]) continue;
dfs(v, u, false);
}
// Solve heavy child and keep its data.
if (heavy[u] != -1) {
dfs(heavy[u], u, true);
}
// Add light subtrees and the current node.
for (int v : adj[u]) {
if (v == p || v == heavy[u]) continue;
addSubtree(v, u, +1);
}
addNode(u, p, +1);
// Now the data represents the entire subtree of u. Compute answer.
ans[u] = distinct; // Example: distinct colors.
// If keep is false, remove this entire subtree's data.
if (!keep) {
addSubtree(u, p, -1);
}
}
void solve() {
dfsSize(0, -1);
dfs(0, -1, false);
}
};
// ==========================================================
// 3) Merging Maps to calculate answers for each node (without Sack)
// ==========================================================
/**
* PURPOSE:
* Merges two maps and updates a global answer variable during the merge.
* This is useful when you need to compute something like the number of equal
* pairs across combined data.
*
* HOW TO USE:
* Call mergeMapAndCompute(a, b, answer) where:
* - a: the larger map (will hold the merged result)
* - b: the smaller map (will be merged into a and cleared)
* - answer: a reference to a variable that stores the computed result
*
* TIME COMPLEXITY:
* O(size_of_smaller_map * log(size_of_larger_map)).
*
* NOTES:
* - The function ensures that the larger map is kept.
* - The example computes the number of equal-value pairs.
* - You must adapt the logic inside the loop to your specific needs.
*
* CONSTRAINTS:
* - Keys must be comparable.
*/
template<typename K, typename V>
void mergeMapAndCompute(map<K,V>& a, map<K,V>& b, long long& answer) {
if (a.size() < b.size()) {
swap(a, b);
// When swapping, you might need to adjust the answer.
// For simplicity, this example recomputes during merge.
}
for (auto &[k, v] : b) {
answer += 1LL * v * a[k]; // Example: count equal pairs.
a[k] += v;
}
b.clear();
}
/**
* PURPOSE:
* Computes for each node the number of equal-value pairs in its subtree
* by merging maps from children into the parent.
*
* HOW TO USE:
* 1. Create an instance: TreeMerger solver(n);
* 2. Add edges with solver.addEdge(u, v).
* 3. Call solver.dfs(0, -1).
* 4. Results are in solver.ans[u] for each node u.
*
* TIME COMPLEXITY:
* O(n log^2 n) in worst case, but often O(n log n) with small-to-large.
*
* NOTES:
* - This uses the small-to-large merging technique on maps.
* - The value of each node is its index (u) in this example.
* - You can change the logic to compute other things (e.g., sum, GCD).
*
* CONSTRAINTS:
* - n must be at least 1.
* - Node IDs from 0 to n-1.
*/
struct TreeMerger {
int n;
vector<vector<int>> adj;
vector<map<int,int>> mp;
vector<long long> ans;
TreeMerger(int n) : n(n), adj(n), mp(n), ans(n, 0) {}
void addEdge(int u, int v) {
adj[u].push_back(v);
adj[v].push_back(u);
}
void dfs(int u, int p) {
mp[u][u] = 1; // Example: value of node is its ID.
for (int v : adj[u]) {
if (v == p) continue;
dfs(v, u);
// Ensure mp[u] is the larger map.
if (mp[u].size() < mp[v].size()) {
swap(mp[u], mp[v]);
// We need to accumulate the answer from the child.
// Since we swapped, the child's answer is now in mp[v]? No,
// ans[v] is the answer for the child. We'll add it later.
}
// Merge mp[v] into mp[u] and update answer for u.
for (auto &[val, cnt] : mp[v]) {
ans[u] += 1LL * cnt * mp[u][val]; // New equal pairs.
mp[u][val] += cnt;
}
// Add the child's answer to the parent's answer.
ans[u] += ans[v];
}
}
};
// ==========================================================
// 4) Advanced techniques: Offline Queries
// ==========================================================
/**
* PURPOSE:
* Handles subtree queries offline using DSU on Tree.
* For example: "How many nodes in subtree of u have value > k?"
*
* HOW TO USE:
* 1. Create an instance: DSUonTreeWithQueries solver(n);
* 2. Add edges with solver.addEdge(u, v).
* 3. Set values for each node in solver.values[u].
* 4. Add queries using solver.addQuery(u, queryID, k).
* 5. Call solver.solve().
* 6. Answers are in solver.ans[queryID].
*
* TIME COMPLEXITY:
* O((n + q) log^2 n) or O((n + q) log n) depending on the data structure used.
*
* NOTES:
* - This uses a Fenwick tree (BIT) to count values.
* - The BIT is updated when nodes are added or removed.
* - "Offline" means all queries are known in advance.
* - The query ID is used to store the answer in the correct order.
*
* CONSTRAINTS:
* - Values should be within the range of the BIT (1..n).
* - Node IDs from 0 to n-1.
* - Query IDs from 0 to q-1.
*/
struct DSUonTreeWithQueries {
struct Query {
int id;
int k;
};
int n;
vector<vector<int>> adj;
vector<vector<Query>> queries;
vector<int> ans;
vector<int> values;
vector<int> bit;
DSUonTreeWithQueries(int n) : n(n), adj(n), queries(n), ans(0), values(n), bit(n+2, 0) {}
void addEdge(int u, int v) {
adj[u].push_back(v);
adj[v].push_back(u);
}
void addQuery(int u, int id, int k) {
queries[u].push_back({id, k});
if ((int)ans.size() <= id) ans.resize(id + 1);
}
// Fenwick tree update.
void updateBIT(int idx, int delta) {
for (int i = idx; i < (int)bit.size(); i += i & -i)
bit[i] += delta;
}
// Fenwick tree query (prefix sum).
int queryBIT(int idx) {
int res = 0;
for (int i = idx; i > 0; i -= i & -i)
res += bit[i];
return res;
}
void addNode(int u, int p, int delta) {
int val = values[u];
if (val >= 1 && val < (int)bit.size()) {
updateBIT(val, delta);
}
for (int v : adj[u]) {
if (v == p) continue;
addNode(v, u, delta);
}
}
void answerQueries(int u) {
int total = queryBIT(n); // Total nodes currently in BIT.
for (auto &q : queries[u]) {
int le = queryBIT(q.k);
ans[q.id] = total - le;
}
}
vector<int> sz, heavy;
void dfsSize(int u, int p) {
sz[u] = 1;
int mx = 0;
for (int v : adj[u]) {
if (v == p) continue;
dfsSize(v, u);
sz[u] += sz[v];
if (sz[v] > mx) {
mx = sz[v];
heavy[u] = v;
}
}
}
void dfs(int u, int p, bool keep) {
for (int v : adj[u]) {
if (v == p || v == heavy[u]) continue;
dfs(v, u, false);
}
if (heavy[u] != -1) {
dfs(heavy[u], u, true);
}
for (int v : adj[u]) {
if (v == p || v == heavy[u]) continue;
addNode(v, u, +1);
}
addNode(u, p, +1);
answerQueries(u);
if (!keep) {
addNode(u, p, -1);
}
}
void solve() {
sz.assign(n, 1);
heavy.assign(n, -1);
dfsSize(0, -1);
dfs(0, -1, false);
}
};
// ==========================================================
// 5) Examples of common problems using Small-to-Large
// ==========================================================
/**
* PURPOSE:
* Computes the Mex (minimum excluded non-negative integer) for each subtree.
*
* HOW TO USE:
* 1. Create an instance: MexDSU solver(n);
* 2. Add edges with solver.addEdge(u, v).
* 3. Call solver.solve().
* 4. Results in solver.ans[u] for each node u.
*
* TIME COMPLEXITY:
* O(n log n) or O(n log^2 n) depending on the implementation.
*
* NOTES:
* - "Mex" is the smallest non-negative integer not present in the subtree.
* - The example uses a simplistic approach that may be slow for mex updates.
* - A better approach uses a set of missing values, but this shows the idea.
*
* CONSTRAINTS:
* - Node values should be in a reasonable range (0..n in this example).
*/
struct MexDSU {
int n;
vector<vector<int>> adj;
vector<int> sz, heavy, ans;
vector<int> freq;
int mex = 0;
MexDSU(int n) : n(n), adj(n), sz(n), heavy(n, -1), ans(n), freq(n+2, 0) {}
void addEdge(int u, int v) {
adj[u].push_back(v);
adj[v].push_back(u);
}
void addNode(int u, int p, int delta) {
int val = u; // Example: value = node ID.
if (delta == 1) {
freq[val]++;
} else {
freq[val]--;
}
// Update mex. This is a simple but potentially slow method.
while (freq[mex] > 0) mex++;
// When removing, mex might decrease; a proper implementation would
// maintain a set of missing values.
for (int v : adj[u]) {
if (v == p) continue;
addNode(v, u, delta);
}
}
void addSubtree(int u, int p, int delta) {
addNode(u, p, delta);
}
void dfsSize(int u, int p) {
sz[u] = 1;
int mx = 0;
for (int v : adj[u]) {
if (v == p) continue;
dfsSize(v, u);
sz[u] += sz[v];
if (sz[v] > mx) {
mx = sz[v];
heavy[u] = v;
}
}
}
void dfs(int u, int p, bool keep) {
for (int v : adj[u]) {
if (v == p || v == heavy[u]) continue;
dfs(v, u, false);
}
if (heavy[u] != -1) {
dfs(heavy[u], u, true);
}
for (int v : adj[u]) {
if (v == p || v == heavy[u]) continue;
addSubtree(v, u, +1);
}
addNode(u, p, +1);
ans[u] = mex;
if (!keep) {
addSubtree(u, p, -1);
}
}
void solve() {
dfsSize(0, -1);
dfs(0, -1, false);
}
};
// ==========================================================
// 6) Extra function: Merge Multiset (add/remove)
// ==========================================================
/**
* PURPOSE:
* Merges two multisets by inserting all elements from b into a.
* After merging, a contains all elements from both, and b is cleared.
*
* HOW TO USE:
* Call mergeMultiset(a, b) where:
* - a: the primary multiset (will hold the final merged result)
* - b: the multiset to merge into a (will be emptied)
*
* TIME COMPLEXITY:
* O(size_of_smaller_multiset * log(size_of_larger_multiset)).
*
* NOTES:
* - The function ensures that the larger multiset is the one kept.
* - Duplicates are preserved (multiset allows duplicates).
*
* CONSTRAINTS:
* - Elements must be comparable.
*/
template<typename T>
void mergeMultiset(multiset<T>& a, multiset<T>& b) {
if (a.size() < b.size()) a.swap(b);
a.insert(b.begin(), b.end());
b.clear();
}
// ==========================================================
// 7) Merge while keeping unique elements (Union)
// ==========================================================
/**
* PURPOSE:
* Merges two unordered_sets by inserting all unique elements from b into a.
* After merging, a contains the union of both sets, and b is cleared.
*
* HOW TO USE:
* Call mergeUnorderedSet(a, b) where:
* - a: the primary unordered_set (will hold the union)
* - b: the unordered_set to merge into a (will be emptied)
*
* TIME COMPLEXITY:
* O(size_of_smaller_set) on average.
*
* NOTES:
* - The function ensures that the larger set is the one kept.
* - Duplicates are automatically removed because unordered_set stores only
* unique elements.
*
* CONSTRAINTS:
* - Elements must be hashable (for unordered_set).
*/
template<typename T>
void mergeUnorderedSet(unordered_set<T>& a, unordered_set<T>& b) {
if (a.size() < b.size()) a.swap(b);
for (auto &x : b) a.insert(x);
b.clear();
}
// ==========================================================
// 8) Merge Vectors with sorting (to reduce complexity)
// ==========================================================
/**
* PURPOSE:
* Merges two sorted vectors into one sorted vector.
*
* HOW TO USE:
* Call mergeSortedVectors(a, b) where a and b are sorted vectors.
* It returns a new vector containing all elements from both, sorted.
*
* TIME COMPLEXITY:
* O(|a| + |b|).
*
* NOTES:
* - Both input vectors MUST be sorted in non-decreasing order.
* - The result is a new vector; the original vectors are unchanged.
*
* CONSTRAINTS:
* - Elements must be comparable.
*/
template<typename T>
vector<T> mergeSortedVectors(const vector<T>& a, const vector<T>& b) {
vector<T> res;
res.reserve(a.size() + b.size());
merge(a.begin(), a.end(), b.begin(), b.end(), back_inserter(res));
return res;
}
// ==========================================================
// 9) Example of merging maps to count equal pairs (improved version)
// ==========================================================
/**
* PURPOSE:
* Computes the number of equal-value pairs in each subtree.
* This is a specialized version of TreeMerger.
*
* HOW TO USE:
* 1. Create an instance: TreeMapMerger solver(n);
* 2. Add edges with solver.addEdge(u, v).
* 3. Call solver.dfs(0, -1).
* 4. Results in solver.ans[u] for each node u.
*
* TIME COMPLEXITY:
* O(n log n) on average.
*
* NOTES:
* - This version correctly handles the accumulation of answers from children.
* - It uses map merging to combine data.
*
* CONSTRAINTS:
* - Node IDs from 0 to n-1.
*/
struct TreeMapMerger {
int n;
vector<vector<int>> adj;
vector<map<int, int>> mp;
vector<long long> ans;
TreeMapMerger(int n) : n(n), adj(n), mp(n), ans(n, 0) {}
void addEdge(int u, int v) {
adj[u].push_back(v);
adj[v].push_back(u);
}
void dfs(int u, int p) {
mp[u][u] = 1; // Example: value of node is its ID.
for (int v : adj[u]) {
if (v == p) continue;
dfs(v, u);
// Merge the smaller map into the larger one.
if (mp[u].size() < mp[v].size()) {
swap(mp[u], mp[v]);
// When swapping, the answer of u might need to be adjusted.
// We'll recompute by merging all child maps.
// For simplicity, we accumulate ans[u] += ans[v] after merging.
}
// Merge mp[v] into mp[u].
for (auto &[val, cnt] : mp[v]) {
ans[u] += 1LL * cnt * mp[u][val];
mp[u][val] += cnt;
}
// Add the child's answer to the parent's answer.
ans[u] += ans[v];
}
}
};
// ==========================================================
// 10) DSU (Union-Find) with Small-To-Large to store elements of each component
// ==========================================================
/**
* PURPOSE:
* Disjoint Set Union (DSU) with small-to-large merging of element lists.
* This allows you to maintain a list of all elements in each component.
*
* HOW TO USE:
* 1. Create an instance: DSU dsu(n);
* 2. Call dsu.unite(a, b) to merge components.
* 3. Access elements of a component via dsu.elements[dsu.find(root)].
*
* TIME COMPLEXITY:
* O(n log n) for all union operations due to small-to-large.
*
* NOTES:
* - "Component" means a set of connected nodes.
* - This is useful when you need to iterate over all elements of a component
* after merges.
* - The elements of a component are stored in a vector.
*
* CONSTRAINTS:
* - Node IDs from 0 to n-1.
*/
struct DSU {
vector<int> parent, sz;
vector<vector<int>> elements;
DSU(int n) : parent(n), sz(n, 1), elements(n) {
iota(parent.begin(), parent.end(), 0);
for (int i = 0; i < n; i++) elements[i].push_back(i);
}
int find(int x) {
if (parent[x] == x) return x;
return parent[x] = find(parent[x]);
}
void unite(int a, int b) {
a = find(a);
b = find(b);
if (a == b) return;
// Merge the smaller component into the larger one.
if (sz[a] < sz[b]) swap(a, b);
// Move all elements from b to a.
for (int x : elements[b]) {
elements[a].push_back(x);
}
elements[b].clear();
parent[b] = a;
sz[a] += sz[b];
}
};
// ==========================================================
// 11) Merging Multisets in the tree (using pointers)
// ==========================================================
/**
* PURPOSE:
* Computes answers for each node using a multiset of values in its subtree.
* For example, you can find the maximum value in each subtree.
*
* HOW TO USE:
* 1. Create a vector of multiset pointers: vector<multiset<int>*> ms(n);
* 2. In your DFS, implement the logic as shown below.
* 3. After processing node u, ms[u] contains all values in its subtree.
*
* TIME COMPLEXITY:
* O(n log n) overall.
*
* NOTES:
* - Uses pointers to avoid copying large multisets.
* - The heavy child's multiset is reused by the parent.
* - After merging, the child's multiset is deleted to free memory.
*
* CONSTRAINTS:
* - The tree must be processed in a DFS.
*/
vector<multiset<int>*> ms;
void dfsMultiset(int u, int p, vector<vector<int>>& adj) {
int bigChild = -1;
for (int v : adj[u]) {
if (v == p) continue;
dfsMultiset(v, u, adj);
if (bigChild == -1 || ms[bigChild]->size() < ms[v]->size())
bigChild = v;
}
if (bigChild == -1) ms[u] = new multiset<int>();
else ms[u] = ms[bigChild];
ms[u]->insert(u); // Add the value of the current node.
for (int v : adj[u]) {
if (v == p || v == bigChild) continue;
// Merge all elements from the small multiset into the large one.
ms[u]->insert(ms[v]->begin(), ms[v]->end());
delete ms[v];
}
// Now ms[u] contains all values in the subtree of u.
// You can answer queries for node u here.
}
// ==========================================================
// 12) "Clear" technique to empty large containers
// ==========================================================
/**
* PURPOSE:
* Efficiently clears a vector and releases its memory.
*
* HOW TO USE:
* vector<T>().swap(myVec);
*
* TIME COMPLEXITY:
* O(size_of_vector).
*
* NOTES:
* - This is a common trick to force immediate memory deallocation.
* - Using myVec.clear() only destroys elements but may keep the capacity.
*/
// Example: vector<int>().swap(myVec);
// ==========================================================
// 13) Example: Calculate GCD for each subtree using map merging
// ==========================================================
/**
* PURPOSE:
* Computes the GCD of all values in each subtree.
*
* HOW TO USE:
* Adapt the TreeMerger or DSUonTree structure to store GCD values.
* For example, you can store a map of value frequencies and compute GCD.
*
* NOTES:
* - GCD is associative, so you can merge results from children.
* - This is just a conceptual example; actual implementation depends on the
* specific problem.
*/
// ==========================================================
// 14) Divide & Conquer with Small-To-Large
// ==========================================================
/**
* PURPOSE:
* Solves range queries on an array using Divide and Conquer combined with
* small-to-large optimization.
*
* HOW TO USE:
* Implement a recursive function solve(l, r) that processes the range [l, r].
* Find the index of the maximum (or minimum) element in the range.
* Recurse on the left and right subranges, then process subarrays that cross
* the pivot. Use small-to-large to iterate over the smaller side.
*
* TIME COMPLEXITY:
* O(n log n) on average.
*
* NOTES:
* - This technique is useful for problems that require counting subarrays
* with certain properties (e.g., where the maximum is at a certain position).
* - The pivot is typically the maximum or minimum element.
* - You need a data structure (like a map) to answer queries quickly.
*/
long long ansDnC = 0;
vector<int> a, pref; // Array and prefix sum.
void solveDnC(int l, int r) {
if (l > r) return;
if (l == r) {
// Handle single element case.
// Example: if a[l] == X && a[l] == S, ansDnC++;
return;
}
// Find index of the maximum element in [l, r].
int idx = l;
for (int i = l; i <= r; i++) {
if (a[i] > a[idx]) idx = i;
}
// Recurse on left and right subranges.
solveDnC(l, idx - 1);
solveDnC(idx + 1, r);
// Count subarrays that cross idx.
if (idx - l < r - idx) {
// Left side is smaller: iterate over left side.
for (int i = l; i <= idx; i++) {
// Use a data structure (e.g., map) from the right side to count.
// Example: ansDnC += mp[need];
}
} else {
// Right side is smaller: iterate over right side.
for (int i = idx; i <= r; i++) {
// Use a data structure from the left side.
}
}
}
// ==========================================================
// 15) Optimized unordered_map merge
// ==========================================================
/**
* PURPOSE:
* Merges two unordered_maps with improved performance by reserving space
* before insertion.
*
* HOW TO USE:
* Call mergeUnorderedMapOptimized(a, b) where:
* - a: the primary unordered_map (will hold the final result)
* - b: the unordered_map to merge into a (will be emptied)
*
* TIME COMPLEXITY:
* O(size_of_smaller_map) on average.
*
* NOTES:
* - Reserves space in a to avoid rehashing during insertion.
* - This can significantly improve performance for large maps.
*
* CONSTRAINTS:
* - Keys must be hashable.
*/
template<typename K, typename V>
void mergeUnorderedMapOptimized(unordered_map<K,V>& a, unordered_map<K,V>& b) {
if (a.size() < b.size()) {
a.swap(b);
}
a.reserve(a.size() + b.size());
for (auto &p : b) {
a[p.first] += p.second; // Or any other merge logic.
}
b.clear();
}
// ==========================================================
// Conclusion: Use these tools wisely, and remember that Small-to-Large
// is a powerful technique for solving tree problems in contests.
// ==========================================================
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
// Example usage:
// DSUonTree solver(n);
// ... add edges ...
// solver.solve();
return 0;
}