shsh's blog

By shsh, history, 7 days ago, In English

NOTE: As always, you can also read this on my blog!


Introduction

Per Wikipedia:

In graph theory, a strong orientation of an undirected graph is an assignment of a direction to each edge that makes it into a strongly connected graph.

A natural question that follows: which undirected graphs have a strong orientation?

Robbins' theorem

Robbins' theorem states that the set of graphs with strong orientations is precisely the set of bridgeless graphs.

Lemma. This condition is necessary (i.e. any graph with a bridge cannot have a strong orientation).

Proof. This picture should make the argument clear:

Bridge condition

If the edge is directed from $$$S \to T$$$, then no node in $$$T$$$ can reach any node in $$$S$$$. Symmetric for $$$T \to S$$$.


We now prove the more difficult half of the theorem:

Lemma. This condition is sufficient (i.e. any bridgeless graph has a strong orientation).

Proof. Robbins' original proof of his theorem introduces a tool called ear decomposition; for more on that, you can check out this Codeforces blog.

Today, I'll present another approach which Wikipedia cites as Boesch and Tindell's. The core claim is as follows:

Consider a mixed graph $$$G$$$ (i.e. with both directed and undirected edges) such that every pair of nodes in $$$G$$$ can reach one another. If there exists an undirected edge $$$(u, v)$$$ in $$$G$$$ that is not a bridge, we always assign a direction to this edge such that all-pairs connectivity is preserved.

If this is true, we can direct all the edges of a bridgeless graph in any order until the entire graph is an SCC.

Mixed graph examples

Two examples of such mixed graphs, and the corresponding orientation (shown in blue) assigned to the edge $$$(u, v)$$$.

Proof of claim. Consider the graph $$$G \setminus {(u, v)}$$$ (i.e. $$$G$$$ with edge $$$(u, v)$$$ removed). We wish to show that in this graph, there always exists a path either from $$$u \to v$$$ or from $$$v \to u$$$. If the former is true, we can direct this edge from $$$v \to u$$$; otherwise, we can direct this edge from $$$u \to v$$$.

Our all-pairs connectivity condition means that all nodes $$$w \in G$$$ must be reachable from $$$u$$$. This means that in $$$G \setminus {(u, v)}$$$, all nodes $$$w$$$ must be reachable from either $$$u$$$ or $$$v$$$. By a similar line of reasoning, all nodes in $$$G \setminus {(u, v)}$$$ must be able to reach either $$$u$$$ or $$$v$$$ as well.

Therefore, all nodes $$$w \in G \setminus {(u, v)}$$$ fall under four categories:

  1. $$$u \to w \to u$$$ (reachable from $$$u$$$, can reach $$$u$$$)
  2. $$$u \to w \to v$$$ (reachable from $$$u$$$, can reach $$$v$$$)
  3. $$$v \to w \to u$$$
  4. $$$v \to w \to v$$$

Four categories of nodes

The four categories of nodes. Note that one node could potentially fall under multiple categories.

Observe that if any nodes of type 2 or 3 exist, our claim is clearly true. Otherwise, all nodes are of either type 1 or type 4, but then $$$(u, v)$$$ would be a bridge which contradicts our assumption. Thus, we have proved the desired claim.

Type 1 and 4 graph

If only type 1 and type 4 nodes exist, the graph looks something like this. The red edge cannot exist in either direction because then a type 2/3 node would exist, which contradicts our assumption.

Full text and comments »

  • Vote: I like it
  • +17
  • Vote: I do not like it

By shsh, history, 13 days ago, In English

While upsolving this problem, I noticed that there appears to be a dearth of editorials; I couldn't find any official ones, and the only unofficial one I found is this one, which is unfortunately rather poorly formatted. Thus, I've decided to try my hand at writing my own editorial for future reference. Feel free to comment with any questions!

NOTE: You can also read this editorial on my blog, which has slightly better formatting.

Basics

First, consider how we might solve the problem if $$$X=Y$$$. Actually, if we let $$$d(u,v)$$$ denote the distance between nodes $$$u$$$ and $$$v$$$, it turns out the following algorithm works:

In order of increasing $$$d(u,X)$$$ and while we have not exceeded the $$$K$$$ threshold, set the closing time of node $$$u$$$ to be $$$d(u,X)$$$.

Proof. We can use a bounding argument.

  1. If we want $$$k$$$ nodes to be reachable from $$$X$$$, then the closing times of each of these nodes $$$u_i$$$ must be at least $$$d(u_i,X)$$$; therefore, the answer is lower-bounded by the sum of the $$$k$$$ smallest $$$d(u,X)$$$.
  2. At the same time, our construction guarantees that all $$$k$$$ nodes can be visited. Roughly, this is because if we select all nodes with $$$d(u,X) \lt D$$$ for some threshold $$$D$$$, there necessarily exists a path using only these nodes to any node with $$$d(u,X)=D$$$ (by definition of distance).

(Note that this approach for $$$X=Y$$$ should apply to general graphs as well, not just trees)

An illustration of this strategy

An illustration of this strategy. The red numbers represent $$$d(u,X)$$$ for each node, and the shaded nodes are the ones for which we set the closing times to $$$d(u,X)$$$. All other closing times are 0.

Subtask 1

Now, let's consider the first subtask in which the distance between $$$X$$$ and $$$Y$$$ is greater than $$$2K$$$. This means that no node can be reachable from both $$$X$$$ and $$$Y$$$, which means we can essentially reuse our solution for $$$X=Y$$$! In particular:

  • Let $$$c(u)=\min(d(u,X),d(u,Y))$$$.
  • Like before, sort by $$$c(u)$$$ and take until we can't.

The proof of correctness is essentially the same as in the $$$X=Y$$$ case.

Subtask 1 scenario

Again, red numbers represent cost and shaded nodes are the reachable ones. In this scenario, the best answer is therefore 5.

Line

Now, consider the case in which the tree is a single line, and $$$X$$$ and $$$Y$$$ are at its ends (but the distance between them can now be less than $$$2K$$$). Now, a node $$$u$$$ could be reachable from both $$$X$$$ and $$$Y$$$ as long as its closing time is at least $$$C(u)=\max(d(u,X),d(u,Y))$$$. Thus, we now have to decide for each node whether it's reachable zero times, once, or twice (corresponding to costs 0, $$$c(u)$$$, and $$$C(u)$$$ respectively).

A possible solution

A possible solution. The square means we want this node to be reachable from both $$$X$$$ and $$$Y$$$, and the blue costs represent $$$C(u)$$$. Therefore, the total cost of this configuration is $$$0+2+6+2+0=10 \le K$$$. The total convenience is 1 + 1 + 2 + 1 + 1 = 6.

First, note that in order for any node to be a square, all nodes on this line must at least be shaded circles first (some of which we can then "upgrade" to squares). Moreover, the nodes upgraded to squares must start from the midpoints and propagate outward. For instance, in the example above, it'd be invalid to have only the second node from the left as a square, and the rest as circles.

Invalid configuration

The midpoints are circled in green (lengths are the same as in the above image). Note that if the second-from-left node is a square, the third-from-left node must also be a square. Therefore, the configuration shown here is invalid.

However, just like before, it turns out that any optimal configuration is actually valid!

Proof. Consider the cost of upgrading each node, $$$C(u)-c(u)$$$. Plotting, it should look something like this:

Upgrade costs

The cost will decrease until the first midpoint, then increase after the second midpoint (costs are shown above the graph in black, and the order of upgrades is indicated by the green numbers). Therefore, upgrades always radiate outward from the center, which corresponds to a valid solution.

Thus, our final algorithm is as follows:

  1. First, upgrade nodes to shaded circles in order of increasing $$$c(u)$$$ while we still can.
  2. If any nodes remain unshaded, terminate.
  3. Otherwise, upgrade circles to squares in order of increasing $$$C(u)-c(u)$$$.

The final answer is $$$(\text{# of shaded circles})+2\times(\text{# of squares})$$$.

Tree

To generalize our line solution, we claim the following solution works for any tree:

  1. First, find the answer under the assumption that no node is reachable twice (this is the Subtask 1 solution).
  2. Otherwise, assume some node is reachable twice: this means that all nodes on the path from $$$X$$$ to $$$Y$$$ must be upgraded at least once.
  3. From here, make any additional upgrades: we claim that any choice of upgrades that minimizes total cost necessarily corresponds to a valid solution.

If the claim in point 3 is true (which we will prove momentarily), the problem reduces to the following:

You are presented with $$$n$$$ boxes. From box $$$i$$$ you can take zero items for cost 0, one item for cost $$$c_i$$$, and two items for cost $$$C_i$$$. It is guaranteed $$$c_i \le C_i$$$. Find the maximum number of items that can be taken given that the total cost must not exceed $$$K$$$.

To solve this, we can split boxes into two types:

  1. When $$$c_i \le C_i-c_i$$$: We can split this box into two single-item boxes with cost $$$c_i$$$ and $$$C_i-c_i$$$ respectively. Note that we should never take the second box without taking the first box, so any valid solution with this reduction corresponds to a valid solution for the original problem as well.
  2. When $$$2c_i \gt C_i$$$: Note that if we take two half-boxes of this type, it's always better to instead take the entire box with smaller $$$C_i$$$.

Two half-boxes

Two half-boxes. A lower-cost solution can be achieved by making the swap indicated by the blue arrow.

Therefore, we will take at most one half-box of this type, and the remaining full boxes should then be taken in order of increasing $$$C_i$$$. So, sorting these boxes by $$$C_i$$$, we will always take a prefix with the exception of at most one element (which should be the element with maximum $$$C_i-c_i$$$ or some later element with minimal $$$c_i$$$).

So, we can just enumerate all prefixes of type 2 boxes, as well as whether we choose one of these boxes to be a half-box, then find the maximum number of type 1 boxes (which have been reduced to single item-boxes) we can take afterward. This can all be done in $$$\mathcal{O}(n \log n)$$$.


Proof of claim. We wish to show that the optimal solution found by the above algorithm (which relaxes the original problem constraints) actually always corresponds to a valid solution under the original constraints as well.

First, let $$$S$$$ be the set of nodes upgraded any number of times in an optimal solution.

Lemma. If the solution is optimal, then $$$S$$$ will be connected.

Proof. Recall that all nodes on the path from $$$X$$$ to $$$Y$$$ are necessarily part of $$$S$$$. For other nodes in $$$S$$$, if its parent (when the tree is rooted at $$$X$$$) is not in $$$S$$$, it cannot be worse to replace this node with its parent.

S connectivity

If $$$S$$$ is not connected, the blue move cannot increase cost.

Now, we just need to show that the selection of squares in an optimal solution leads to a valid configuration as well. This can be shown as follows:

  1. First, decide $$$S$$$ and upgrade all nodes in $$$S$$$ to circles.
  2. Then, additionally upgrade some of these nodes to squares in increasing order of $$$C(u)-c(u)$$$.

The key thing to note here is that within each subtree hanging off the path from $$$X$$$ to $$$Y$$$, the value of $$$C(u)-c(u)$$$ is constant.

Subtree upgrade costs

Each subtree is shown with the upgrade cost corresponding to every node in that subtree.

Therefore, just as before, our upgrades will radiate from the center subtree outward. Furthermore, because upgrade cost is constant within a subtree, we can always upgrade in a way such that shallower nodes are upgraded first. It can be shown that these conditions will always result in a valid solution.

Code

My QOJ submission

Full text and comments »

  • Vote: I like it
  • +37
  • Vote: I do not like it

By shsh, history, 2 months ago, In English

Thank you to everyone who attended this year's Harker Programming Invitational (HPI)! If you haven't already, we would really appreciate it if you filled out our feedback form so we can make our contest even better next year. Also, Novice and Advanced problems have been uploaded to the Gym for upsolving.

Note: Only solutions for the Advanced division are presented here, but solutions for Novice-only problems (Yash is Cross-Eyed, Repetition, Skills) can be provided upon request.

Harker!!

Solution
Code (Team Aarav)

String Runs

Solution
Code (eevee0)

Prepared by TheYashB

The Penguin-Gopher Shuffle

Hint 1
Hint 2
Solution
Code (Team Aarav)

Prepared by TheYashB

Regina's Task

Hint
Solution
Code (shsh)

Prepared by andrewgopher

Tung Tung String

Solution
TL;DR
Code (shsh)

Prepared by shsh

Pace Pushers

Solution
Code (AksLolCoding)

Prepared by noodlesoodles

Skating with Alysa Liu

Hint 1
Hint 2
Solution
Code (shsh)

Prepared by TheYashB

Looksmaxxing with Clavicular

For concision, I will use the term "good stream" to refer to a stream which stream-mogs Clavicular.

Hint
Solution
Code (shsh)

Prepared by shsh

Daniel Saves Yash

Hint
Solution
Code (shsh)

Prepared by andrewgopher

Tree Queries

Hint 1
Hint 2
Solution
Code (shsh)

Prepared by shsh

Full text and comments »

Tutorial of HPI 2026 Advanced
Tutorial of HPI 2026 Novice
  • Vote: I like it
  • +28
  • Vote: I do not like it

By shsh, history, 3 months ago, In English

Hello Codeforces,

The Harker Programming Club is excited to announce that registration for HPI 2026 has opened! The event will be held at the Harker Upper School campus on Saturday, March 7, 2026. This year the theme of our contest will be CS & Linguistics. Teams of up to 3 students may participate in either our Novice or Advanced Division.

The details of this year's event are listed below:

  • Event Name: Harker Programming Invitational 2026
  • Date: March 7, 2026 (Saturday)
  • Time: 8 AM-12:30 PM PST
  • Participants: All competitors of any age are allowed. If you cannot participate in-person, there is an online version.
  • Location: The Harker Upper School campus (500 Saratoga Ave, San Jose, CA 95129)
Detailed schedule
Flyers

To sign up for HPI, please visit tinyurl.com/hpi2026. In-person spots are limited, so grab one while you can! All problems were written and prepared by shsh, TheYashB, andrewgopher, Insert_Username_Here, and reginaz.

Please email programmingclub@students.harker.org or leave a comment on this blog if you have any questions, and we hope to see you at HPI 2026!

FAQ

How difficult are the problems?

The estimated CF ratings of the Novice problems range from 800 to 1900 (USACO Bronze to Gold).

For Advanced, the estimated ratings range from 800 to 2600 (USACO Bronze to Platinum).

Can I participate online?

Yes, but you will not be eligible for prizes.

Will there be food?

Yes!

Are templates allowed?

Yes!

Is generative AI allowed?

No. Please do not use AI even to look up language documentation, as this will bring all your submissions under suspicion.

Is the contest ICPC format?

No. Every team member is allowed to code and submit from their own computer.

Will tourist be attending?

Hopefully!

Full text and comments »

  • Vote: I like it
  • +22
  • Vote: I do not like it

By shsh, history, 6 months ago, In English

Check this submission: 349307834.

The reason I'm making this post is because the hack I used was very rudimentary, with $$$t = n = q = 1$$$. Shouldn't such a test be included in pretests/systests? Personally, if I had achieved rank 1 in contest but then gotten hacked by such a simple countercase, I would be distraught.

Full text and comments »

  • Vote: I like it
  • +63
  • Vote: I do not like it

By shsh, history, 6 months ago, In English

What sequences of union/find queries will achieve the worst case complexity of a DSU? I'm specifically interested in a DSU with path compression but without union by rank/size, which has worst case complexity $$$\mathcal{O}(m \log n + n)$$$, but constructions for other variations are welcome as well.

A nice proof for the $$$\mathcal{O}(m \log n + n)$$$ bound would also be appreciated.

Full text and comments »

Tags dsu
  • Vote: I like it
  • +62
  • Vote: I do not like it

By shsh, history, 7 months ago, In English

Problem: 2155F - Juan's Colorful Tree

For this problem, it turns out that if you just iterate over the colors in the smaller of the two sets $$$C_u$$$ and $$$C_v$$$ (and also de-dupe queries), total complexity is bounded by something like $$$s \sqrt {q}$$$. More generally, let's say we have an array $$$a_i$$$ such that $$$\sum a_i = s$$$. If we pick $$$q$$$ distinct pairs of indices $$$x_i \leq y_i$$$, then it turns out that over all possible choices, $$$\sum \min(a_{x_i}, a_{y_i})$$$ is bounded by $$$s \sqrt {q}$$$. Equality is achieved when $$$a$$$ has $$$\sqrt{q}$$$ values equal to $$$\frac{s}{\sqrt{q}}$$$, and all other values equal to $$$0$$$.

Are there any other problems that use this very interesting bound?

Full text and comments »

  • Vote: I like it
  • +53
  • Vote: I do not like it

By shsh, history, 9 months ago, In English

Problem: given a weighted, connected, undirected graph $$$G$$$, find a spanning tree that minimizes the LCM of its weights.

Is this solvable in polynomial time? Or, can we prove that it's not?

Full text and comments »

Tags mst
  • Vote: I like it
  • +20
  • Vote: I do not like it

By shsh, history, 10 months ago, In English

Given two weighted, undirected graphs on $$$n$$$ nodes $$$G_1$$$ and $$$G_2$$$, is it possible to efficiently find a tree on these $$$n$$$ nodes that is an MST of both $$$G_1$$$ and $$$G_2$$$?

UPD: Building on chromate00's comments, it turns out that we can simply construct a new graph $$$G$$$ where the weight of each edge is the sum of that edge's weights in $$$G_1$$$ and $$$G_2$$$, and find the MST on this graph (in chromate00's notation here, this means we can actually just always set $$$C = 1$$$). If a common MST exists, we can guarantee that this algorithm will always find it.

Proof. Let $$$W_1$$$, $$$W_2$$$, and $$$W$$$ denote the total weight of MSTs for $$$G_1$$$, $$$G_2$$$, and $$$G$$$ respectively.

  • Note that $$$W_1 + W_2 \leq W$$$.
  • Also note that the weight of any spanning tree in $$$G_1$$$ is at least $$$W_1$$$, and similarly for $$$G_2$$$. Therefore, if we find a spanning tree $$$T$$$ with exactly $$$W = W_1 + W_2$$$, it must follow that $$$T$$$ has exactly weight $$$W_1$$$ in $$$G_1$$$ and exactly weight $$$W_2$$$ in $$$G_2$$$, since these are the minimum possible.

Here's a problem that uses this idea: 1054G - New Road Network

Hint
Solution

(This writeup is also available on my personal blog!)

Full text and comments »

  • Vote: I like it
  • +62
  • Vote: I do not like it

By shsh, history, 10 months ago, In English

As I understand it, the Sprague-Grundy theorem states that, under the operation of combining games in parallel, every impartial game can be reduced to a game of Nim. I see that the definition itself specifies this parallel combination operator explicitly: two impartial games G and G' are assigned the same nimber iff for all games H, G + H and G' + H have the same outcome (here, + denotes the parallel combination operator)---this definition is from Wikipedia.

However, what about other methods of combining games? For instance, we combine two games A and B such that they are still played in parallel, but as soon as a player has no moves left in A, they instantly lose, regardless of the position of B. In general, can we still treat games A and B as their corresponding nimbers? If so, why? If not, what's a counterexample?

EDIT (which I added in a comment below):

More precisely, my question is:

Let $$$n(G)$$$ be the nimber associated with game G, according to the Sprague-Grundy theorem. Moreover, let $$$f(G, H)$$$ be a function of any two games that outputs another game. Then, I would like to know: for all possible $$$f$$$, does $$$n(f(G, H)) = f(n(G), n(H))$$$?

Full text and comments »

  • Vote: I like it
  • +9
  • Vote: I do not like it

By shsh, history, 12 months ago, In English

In every virtual tree implementation I can find (https://en.oi-wiki.org/graph/virtual-tree/, https://mirror.codeforces.com/blog/entry/140066 , for instance), there seems to be an unnecessarily complex method to construct edges involving some kind of stack. The stack method used here is thankfully much simpler than both of these, but can't we simplify it even further to not use a stack at all?

That is, instead of:

for (int i = 1; i < (int)res.size(); i++) {
	while (tin[res[i]] > tout[stk.back()]) { stk.pop_back(); }

	vadj[stk.back()].push_back(res[i]);
	stk.push_back(res[i]);
}

(From the USACO Guide implementation)

We could just write:

for (int i = 1; i < (int)res.size(); i++) {
	vadj[lca(res[i - 1], res[i])].push_back(res[i]);
}

Is there any case in which this approach is wrong? I made this modification and still got accepted on the relevant AtCoder problem.

Edit: the usaco.guide module has since been updated, but I'm still not sure what advantage the second implementation on OI Wiki here has over the first one (which matches more closely with what I've described here), since both implementations seems to have O(n log n) runtime.

Full text and comments »

  • Vote: I like it
  • +21
  • Vote: I do not like it