Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
Forget “tree” for a second. Build the graph of all allowed edges. A valid tree exists exactly when this graph is connected, because any connected graph has a spanning tree.
Read the permutation from left to right. When value appears, its only possible edges to already-seen vertices are to values smaller than . Earlier-but-larger vertices are useless for right now.
For each connected component among the already-seen vertices, you only need to know its minimum label. If that minimum is below , then the component contains some vertex smaller than , so can attach to it.
If a component has minimum , the edge is definitely legal: was seen earlier, and . If its minimum is greater than , then every vertex in it is greater than , so cannot connect to that component.
Maintain the current component minima in a sorted set. For each , remove every minimum smaller than , output edges from those minima to , then insert the minimum of the merged component. At the end, one component means Yes; otherwise No.
Core Idea
Make the implicit graph where vertices are , and an edge with exists iff appears before in the permutation.
The problem asks for a tree using only valid edges. That exists iff this graph is connected. If it is connected, any spanning tree works. If it is disconnected, no tree can magically teleport across components. Sad, but math is rude like that.
So we just need to construct a spanning forest of this graph efficiently, and check whether it has one component.
Process The Permutation Left To Right
Suppose we are currently reading value .
All previously read values appeared before in the permutation. For an already-seen value :
So when arrives, it connects exactly to the already-processed values smaller than .
That is the entire trick. No DP circus needed.
Representing Components
Look at the connected components formed by the values we have processed so far.
For a component , when does the new value connect to it?
It connects iff contains some value smaller than .
That is equivalent to:
So each current component only needs one piece of information: its minimum label.
If a component has minimum , then we can safely add the edge :
This merges with that whole component. If several components have minima smaller than , merges all of them, and we output one edge per merged component.
If a component has minimum greater than , then all labels in that component are greater than , so has no edge to it right now.
Algorithm
Maintain a sorted set comps, containing the minimum label of every current component.
For every value in permutation order:
comps is less than :
comps.At the end:
comps.size() == 1, the graph is connected, so print Yes and all chosen edges;No.Why The Edges Form A Tree When Connected
Every printed edge connects two different current components. So we never create a cycle.
Each printed edge reduces the number of components by . After all vertices are processed, if exactly one component remains, then we printed exactly edges. A connected acyclic graph with edges is a tree. Boom.
Complexity
Each printed edge corresponds to one removed component minimum. There are at most such removals. Each set operation costs .
Total complexity per test case is:
Memory usage is .
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<int> p(n);
for (int &x : p) cin >> x;
set<int> comps;
vector<pair<int, int>> edges;
for (int x : p) {
int mn = x;
vector<int> merged;
auto it = comps.begin();
while (it != comps.end() && *it < x) {
merged.push_back(*it);
it = comps.erase(it);
}
for (int m : merged) {
edges.push_back({m, x});
mn = min(mn, m);
}
comps.insert(mn);
}
if ((int) comps.size() != 1) {
cout << "No\n";
} else {
cout << "Yes\n";
for (auto [u, v] : edges) {
cout << u << ' ' << v << '\n';
}
}
}
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<int> p(n);
for (int &x : p) cin >> x;
set<int> comps;
vector<pair<int, int>> edges;
for (int x : p) {
int mn = x;
vector<int> merged;
auto it = comps.begin();
while (it != comps.end() && *it < x) {
merged.push_back(*it);
it = comps.erase(it);
}
for (int m : merged) {
edges.push_back({m, x});
mn = min(mn, m);
}
comps.insert(mn);
}
if ((int) comps.size() != 1) {
cout << "No\n";
} else {
cout << "Yes\n";
for (auto [u, v] : edges) {
cout << u << ' ' << v << '\n';
}
}
}
}