In this post, I want to share my approach to solving Problem E: Build a Tree from Codeforces Round 1111.
The problem asks us to construct a tree with $$$n$$$ vertices such that $$$\sum_{i=1}^{n} dist(i, (i \bmod n) + 1) = k$$$.
Let $$$dist(i, j) = dep(i) + dep(j) - 2 \cdot dep(LCA(i, j))$$$.
Summing this over the cycle $$$i=1 \dots n$$$:
Key takeaways from this equation:
$$$k$$$ must be even: Since the RHS is multiplied by $$$2$$$, the total sum $$$k$$$ must be an even number. If $$$k$$$ is odd, no such tree exists.
Minimum $$$k$$$ is $$$2(n-1)$$$: This occurs when the tree is a "star graph" (all nodes connected to the root), which keeps depths minimal.
Maximizing $$$k$$$: To reach a higher $$$k$$$, we need to maximize depths and minimize the $$$dep(LCA)$$$ terms. The LCA of adjacent nodes $$$(i, i+1)$$$ becomes $$$0$$$ (the root) if we separate all even-numbered nodes and odd-numbered nodes into two distinct branches.
If we want a score $$$k$$$ that is higher than our baseline of $$$2(n-1)$$$, we have to stretch the tree out. Because we are keeping all even nodes on one side and all odd nodes on the other, the LCA for any adjacent pair is always the root (depth 0). This means we don't have to worry about subtractions, and our score formula simplifies to:
Every time we increase a node's depth by 1, the sum $$$\sum dep_i$$$ goes up by 1. Because of the multiplier in the formula, our total score $$$k$$$ goes up by exactly 2.
This means for every 1 unit of "extra depth" we create, $$$k$$$ goes up by 2. So, how much total extra depth do we need to reach our target $$$k$$$?
We have our target extra depth req, but we must build our tree using two separate buckets to ensure the LCA of adjacent nodes is always the root:
Even nodes bucket: size $$$e = n / 2$$$
Odd nodes bucket: size $$$o = (n - 1) / 2$$$
A bucket of size $$$m$$$ can at most form a single straight chain, generating a maximum extra depth of $$$\frac{m(m-1)}{2}$$$.
First, we check if req exceeds the combined maximum capacities of both our even and odd buckets. If it does, it is impossible to reach $$$k$$$, and we output -1.
If it is possible, we can just be greedy! We assign as much of req as possible to the even bucket (up to its maximum capacity), and assign whatever depth is remaining to the odd bucket. Then, we process both buckets independently using the logic below!
We need to generate exactly our assigned extra depth (let's call it t) for a specific bucket. We do this by building a straight line (a chain) of nodes.
Let's look at how much extra depth a chain of val nodes generates. (Remember, depth 1 gives 0 extra depth, depth 2 gives 1 extra depth, etc.):
- 1st node in chain (depth 1) -> generates 0 extra depth
- 2nd node in chain (depth 2) -> generates 1 extra depth
- 3rd node in chain (depth 3) -> generates 2 extra depth
- 4th node in chain (depth 4) -> generates 3 extra depth
If our chain has $$$X$$$ nodes, the extra depth it creates is the sum: $$$0 + 1 + 2 + \dots + (X - 1)$$$ =
So, to reach our target t without overshooting it, we just loop and find the largest possible val (chain length) where:
Unless we are incredibly lucky, our chain won't perfectly hit t. We will have a small shortage. Let's call this shortage rem.
We just take our next unused node and plug it directly into the chain at the specific depth (rem)!
- If we need 2 more extra depth, we plug a node in so it sits at depth 3.
- If we need 4 more extra depth, we plug a node in so it sits at depth 5.
We plug them all directly back into the root (Node 1). This keeps them at depth 1, meaning they generate 0 extra depth and don't ruin our perfectly calculated score.
void tree(vi& V,ll t){
ll m = V.size();
if (m == 0) return;
ll val = 0;
while((val + 1)*val/2 <= t) val++;
// Step 1: Build the main chain
for(int i=0;i<val;i++){
if(i == 0) cout << V[i] << " 1\n";
else cout << V[i] << " " << V[i - 1] << "\n";
}
// Steps 2 & 3: Handle the remainder and leftovers
if(val < m){
ll rem = t - val*(val - 1)/2;
// Step 2: Attach one node to get exactly 'rem' extra depth
if(rem == 0) cout << V[val] << " 1\n";
else cout << V[val] << " " << V[rem - 1] << "\n";
// Step 3: Attach all remaining unused nodes directly to the root (0 extra depth)
for(int j=val+1;j<m;j++) cout << V[j] << " 1\n";
}
}
void solve(){
int n; ll k; cin >> n >> k;
if(k < 2*(n-1) || k%2 == 1){ cout << -1 << '\n'; return; }
ll e = n/2; // Number of even nodes
ll o = (n - 1)/2; // Number of odd nodes
// Max capacity of extra depth if both buckets formed single straight chains
ll mx = e*(e-1)/2 + o*(o-1)/2;
ll req = (k - 2*(n-1))/2;
if (req > mx){ cout << -1 << '\n'; return; }
ll even = min(req,e*(e-1)/2);
ll odd = req - even;
vi a,b;
for(int i=2;i<=n;i += 2) a.push_back(i);
for (int i=3;i<=n; i += 2) b.push_back(i);
tree(a,even);
tree(b,odd);
}








Amazing!!
Insightful
goat
Wow!! Amazing explanation!
osm
It's a really good explanation <._.>
Great Explanation. Thanks