← All problems

Decidophobia

Codeforces · Round #1105 (Div. 1) · Problem B

Problem

There are nn people at a round table, numbered 1..n1..n clockwise, and person ii has a weight A[i]A[i]. You may give a gift to any subset of them. Each person's field of view is the DD people clockwise and the DD people counter-clockwise from them (2D2D people in total).

  • If person ii receives a gift and xx people in their field of view did not, they gain xA[i]x \cdot A[i] happiness.
  • If person ii does not receive a gift and xx people in their field of view did, they lose xA[i]x \cdot A[i] happiness.

Choose who to gift so that the total happiness is maximized.

Initial Observations
  1. Graph.
  2. Flow?
  3. Dynamic programming.
  4. Double nn to handle wrap-around.
  5. Greedy construction?
  6. Use long longs (really big numbers).
  7. Sweep... (starting from where?)
  8. Consider intervals / windows [iD,i+D][i-D, i+D].
  9. The answer is always 0\geq 0 (gift nobody).
  10. Sort by A[i]A[i]?
  11. 2-SAT?
IdeaCuts, flows, and augmenting paths?

I originally thought of this as potentially a dynamic programming problem — but DP usually wants an ordering, and there is no natural ordering here, because of the cyclical nature of the problem (the candidates literally sit in a circle around a table).

Pursue Extremes

One possible "natural ordering" would be to start with the smallest or largest element.

Write It Out

I started playing around with the score formula (the total happiness — I'll just call it the score from here on) and found some interesting observations.

Key Observation

Suppose person ii is gifted, person jj is ungifted, and they are neighbors (within distance DD around the circle). Then this pair contributes exactly A[i]A[j]A[i] - A[j] to the total score — and the whole score is the sum of A[i]A[j]A[i] - A[j] over all such gifted–ungifted neighbor pairs.

Proof.

Rewrite the formula. A gifted person ii contributes xA[i]x \cdot A[i] where xx is their number of ungifted neighbors — that is, +A[i]+A[i] once per ungifted neighbor jj. Symmetrically, an ungifted jj contributes A[j]-A[j] once per gifted neighbor. Grouping the two contributions by pair: each gifted–ungifted neighbor pair (i,j)(i, j) contributes A[i]A[j]A[i] - A[j], and nothing else contributes at all.

(We will be using this observation almost as a given from here on.)

Draw from Experience

From here, the problem reminded me of the min-cut / max-cut problem.

Problem Transformation

Max Cut: given a graph with costs on the edges (possibly negative), select a subset SS of nodes; for each edge (i,j)(i,j) with iSi \in S and jSj \notin S, add its cost to the total; maximize.

In a way, our problem is a max-cut problem on the circle-window graph. However, Max Cut is NP-hard in general (I believe), so this can't work as-is for a linear-time target — but specialized graphs often admit cleaner solutions, and the min-cut problem (non-negative weights, minimize) is polynomial via the max-flow min-cut theorem. So I started wondering: can I solve this with a max-flow algorithm on this graph, or some modified variant? The graph is also highly symmetric, since each node's neighbors are just the interval [iD,i+D][i-D, i+D] around it.

Draw from Experience

The core of max-flow (and of relatives like bipartite matching) is the augmenting path: given a current selection with some score, incrementally improve it — typically via a chain of additions and removals that individually might not help but collectively do. Given the structure here, I figured this might lead to a greedy or incremental approach.

Three candidate algorithms came out of this:

  1. Greedy. Start with nobody gifted. Repeatedly pick a person and gift them if that increases the total; otherwise leave them ungifted.
  2. Augmenting path. Start with nobody gifted. When gifting person ii hurts because some ungifted neighbor jj contributes a large negative, try gifting jj too; or if some gifted neighbor's contribution just dropped to zero, try un-gifting them — a BFS-like search for a chain of flips that collectively improves the score, like the augmenting step in max-flow.
  3. Greedy sweep, largest first. Gift people in decreasing order of A[i]A[i], as long as each addition doesn't decrease the score.

The first is just a greedy simplification of the second, and given the symmetry of the problem it felt like a simpler variant should work here; the third exploits the symmetry a bit more.

Examine Examples

At this point I played with the sample test cases, [1 2 3][1\ 2\ 3] and [1 4 5 2 6][1\ 4\ 5\ 2\ 6], to get a feel for what an optimal assignment looks like and where an augmenting path might hide. For instance, on [1 4 5 2 6][1\ 4\ 5\ 2\ 6] with D=1D = 1 (each person sees only their immediate neighbors):

          1
        /   \
       6     4        gifted:   {4, 5, 6}
       |     |        ungifted: {1, 2}
       2 --- 5
 
pairs:  4-1 = 3,  5-2 = 3,  6-2 = 4,  6-1 = 5     total = 15

(For contrast: gift everyone and the total is 00 — no gifted–ungifted pairs exist at all.)

There's a Key Question hiding in here that I should have asked myself — "under what conditions does gifting a person actually increase the score versus decrease it?" — but I did not, and kept poking at examples instead.

I spent most of my time exploring variants of the three algorithms above. Ironically (spoiler alert), it turns out that the greedy algorithm actually does work here, but I wasn't able to prove it by now. I kept flip-flopping between the greedy and the augmenting-path framing, trying to understand what could work. More or less got stuck.

What kept getting me: the relationship between gifting some person ii and un-gifting some person jj seemed complicated, because the neighborhoods all overlap with no obvious starting point. I kept playing with the A[i]A[j]A[i] - A[j] versus A[j]A[i]A[j] - A[i] relationships and it was stumping me.

IdeaWrite out the greedy / augmentation stepAC

In Idea 1, through a variety of framings, we arrived at some kind of greedy algorithm (gift a person if it increases the score) or an augmenting-path variant (gift some, un-gift others) — with not much luck making either into a working solution. Here are some observations made along the way:

Observation.

Any person whose weight is \geq all of their neighbors' weights should always be gifted. Why: leaving them ungifted produces a negative (or zero) term against every gifted neighbor, while gifting them produces only zero-or-positive terms against every neighbor. The symmetric argument says a person whose weight is \leq all of their neighbors' should always be ungifted.

This was good — it started to imply even more of a greedy structure.

Examine Examples

Looking at [1 4 5 2 6][1\ 4\ 5\ 2\ 6], an optimal solution is to gift 4,5,64, 5, 6 and leave 1,21, 2 ungifted, for a score of 1515. Oddly enough, we gifted all of the highest elements and skipped all of the smallest. There's definitely a there-there. But why?

Examine Examples

Let's try a more extreme example: [20 7 6 5 6 7][20\ 7\ 6\ 5\ 6\ 7] with D=1D = 1. Clearly we want to gift the 2020 and skip the two 77's around it, collecting 1313 per pair — 2626 total. I then wanted to also gift the two 66's and skip the 55 between them for a couple more points — but check it carefully: each 66 gains +1+1 against the 55 and loses 1-1 against its neighboring 77, so those flips are exactly break-even. The optimum is 2626, and the 66's can go either way. So the dumb "gift the largest, skip the smallest" picture is already subtler than it looks.

Overall though, intuitively, it felt like there was symmetry in the constraints waiting to be exploited. It all hinged on when we should greedily gift an element versus not, based on the configuration of its neighbors — and I didn't have a good answer for that.

After struggling with this long enough, I finally asked myself the Key Question...

Key Question

Under what conditions does gifting person ii improve the score, compared to leaving them ungifted?

Write It Out

To answer that, let's just actually write it down.

Consider person ii: say they have mm gifted neighbors with weights X1,,XmX_1, \ldots, X_m, and kk ungifted neighbors with weights Y1,,YkY_1, \ldots, Y_k. Using Key Observation 1, the total contribution of all pairs involving ii:

If ii is ungifted, each gifted neighbor forms a pair with ii:

Contributionungifted=(X1A[i])++(XmA[i])=j=1mXjmA[i]\text{Contribution}_{\text{ungifted}} = (X_1 - A[i]) + \cdots + (X_m - A[i]) = \sum_{j=1}^{m} X_j - m \cdot A[i]

If ii is gifted, each ungifted neighbor forms a pair with ii:

Contributiongifted=(A[i]Y1)++(A[i]Yk)=kA[i]j=1kYj\text{Contribution}_{\text{gifted}} = (A[i] - Y_1) + \cdots + (A[i] - Y_k) = k \cdot A[i] - \sum_{j=1}^{k} Y_j

It's better to gift ii iff Contributiongifted>Contributionungifted\text{Contribution}_{\text{gifted}} > \text{Contribution}_{\text{ungifted}}, so we literally just write it out:

kA[i]jYj>jXjmA[i](m+k)A[i]>jXj+jYjA[i]>jXj+jYjm+k\begin{aligned} k \cdot A[i] - \sum_j Y_j &> \sum_j X_j - m \cdot A[i] \\ (m + k) \cdot A[i] &> \sum_j X_j + \sum_j Y_j \\ A[i] &> \frac{\sum_j X_j + \sum_j Y_j}{m + k} \end{aligned}

Miraculously, this simplified with A[i]A[i] alone on one side. And the really cool part: Xj+Yj\sum X_j + \sum Y_j is just the sum over all neighbors of ii — gifted or not — and m+k=2Dm + k = 2D is the total number of neighbors. The gifted/ungifted split canceled out entirely.

Key Observation

Person ii should be gifted iff A[i]12DjN(i)A[j]A[i] \geq \frac{1}{2D}\sum_{j \in N(i)} A[j] — iff their weight is at least the average of all their neighbors' weights. It doesn't even matter what configuration the others are in: each person's decision is independent.

This all follows from writing out the formula explicitly and simplifying. It yields a pretty nice algorithm:

AlgorithmGreedy selection
Given n, A[1..n], and D
 
# pick the optimal set using the greedy rule
for each person i:
    sum_of_neighbors = sum of A[j] over the 2D neighbors j of i
                       (wrap around the circle; exclude i itself)
    if A[i] >= sum_of_neighbors / (2D):  gift person i
 
# compute the answer with the original formula
answer = 0
for each person i:
    if i gifted:  answer += A[i] * (ungifted neighbors of i)
    else:         answer -= A[i] * (gifted neighbors of i)

At this point we're effectively done, and it's a matter of implementing this in O(n)O(n), using two relatively standard techniques:

Key Observation

In a regular array, a cumulative-sum array SS (with S[i]=A[1]++A[i]S[i] = A[1] + \cdots + A[i]) gives any range sum as S[j]S[i1]S[j] - S[i-1] (being careful with the edge cases).

Key Observation

To handle a circular array, "triple" it: set A[in]=A[i]=A[i+n]A[i - n] = A[i] = A[i + n] (with suitable shifts to keep indices non-negative), then build the cumulative sums on the result. Any window of neighbors, forwards or backwards across the wrap, becomes an ordinary range.

Observation.

Counting the gifted neighbors of each person works the same way — a cumulative count over the tripled gift-indicator array.

So here is the final algorithm with all the details:

AlgorithmPicking the optimal set, O(n)
Given n, A[1..n], and D
X[i] := 1 iff person i is gifted;  X[1..n] = 0
 
triple A for wrap-around:  A[1..3n] = A + A + A
prefix sums:               S[i] = S[i-1] + A[i]   for i = 1..3n
 
for each person i (using tripled index i+n):
    # the window [i+n-D, i+n+D] covers 2D+1 people INCLUDING i — subtract i
    sum_of_neighbors = S[i+n+D] - S[i+n-D-1] - A[i+n]
    if A[i] >= sum_of_neighbors / (2D):  X[i] = 1
 
triple X for wrap-around:  X[1..3n] = X + X + X
prefix counts:             cnt[i] = cnt[i-1] + X[i]  for i = 1..3n
 
answer = 0
for each person i (using tripled index i+n):
    gifted_neighbors = cnt[i+n+D] - cnt[i+n-D-1] - X[i+n]   # exclude i again
    ungifted_neighbors = 2D - gifted_neighbors
    if X[i]:  answer += A[i] * ungifted_neighbors
    else:     answer -= A[i] * gifted_neighbors
 
print answer

(Implementation notes: compare 2DA[i]sum_of_neighbors2D \cdot A[i] \geq \text{sum\_of\_neighbors} instead of dividing, and use 64-bit integers throughout — the totals get big.)

Tada!

At this point we have a correct solution, but Key Observation 2 did not sit well with me. Why? Because it didn't seem intuitive — and still doesn't quite — that a person must be gifted iff their weight exceeds the average of all their neighbors. It seemed to just miraculously pop out of the algebra, which I didn't fully understand beyond "it just works." But... why? Can we think this through to a more intuitive understanding — one I could have reached faster?

Key Question

Is there a more intuitive reason why a person is gifted iff their weight is at least the average of their neighbors'?

Key Observation

Yes — it still requires writing it out, but it's more satisfying. Suppose person ii is ungifted, and we switch them to gifted. What happens to the contribution with each individual neighbor jj? If jj is gifted, the old pair contributed A[j]A[i]A[j] - A[i] and the new pair contributes 00: the change is +(A[i]A[j])+(A[i] - A[j]). If jj is ungifted, the old pair contributed 00 and the new one contributes A[i]A[j]A[i] - A[j]: the change is +(A[i]A[j])+(A[i] - A[j]) again. The change is +(A[i]A[j])+(A[i] - A[j]) for every neighbor, regardless of whether jj is gifted.

Therefore the total change from gifting person ii is:

jN(i)(A[i]A[j])=2DA[i]jN(i)A[j]\sum_{j \in N(i)} (A[i] - A[j]) = 2D \cdot A[i] - \sum_{j \in N(i)} A[j]

which is positive iff

A[i]>12DjN(i)A[j]A[i] > \frac{1}{2D} \sum_{j \in N(i)} A[j]

— exactly the condition we derived earlier. It's effectively the same formula, but now we can see the core of why it works. It feels more like an "aha!" to me: gifting ii always adds A[i]A[j]A[i] - A[j] across all neighbors jj, so if A[i]A[i] beats the neighbor average, the sum of those terms is positive — independent of everyone else's gifts.

And here we are. I think we're done.

Review

Each person is gifted iff their weight is at least the average of their 2D2D neighbors' weights — an independent, per-person rule — and prefix sums over a tripled array make the whole thing O(n)O(n). The path:

  1. Rewrite the score as pairs. Every gifted–ungifted neighbor pair (i,j)(i, j) contributes exactly A[i]A[j]A[i] - A[j], and that's the whole formula. [1]
  2. The problem smells greedy / augmenting-path-like, from its similarity to cut problems and from the symmetry of the circle and the DD-windows.
  3. The Key Question: what happens when you switch one person from ungifted to gifted? (Equivalently: when is it better for a person to be gifted?)
  4. Writing out the formulas, the split cancels: person ii should be gifted iff A[i]A[i] is at least the average of all their neighbors' weights, independent of everyone else's status. This is the most important observation in the problem. [2]
  5. Cumulative sums + a triple-concatenated array handle the circular windows in O(1)O(1) per person. [3][4]
  6. (Bonus) The change-in-contribution view derives the same rule more intuitively: gifting ii changes the score by exactly j(A[i]A[j])\sum_{j}(A[i] - A[j]) over neighbors, regardless of their status. [5]

References

Problem-solving techniques used:

Examine Examples

As per usual, a good place to start — working small examples by hand developed the intuition that the size of A[i]A[i] is critical, though with some nuances.

Write It Out

Arguably the most important technique for this problem. Once we had some greedy/augmentation approach in hand, we would have saved a LOT of time by immediately writing out the algebra for "what exactly happens to the contribution of ii when ii goes from ungifted to gifted." The math and the intuition simplified beautifully.

Pursue Extremes

Somewhat helpful for looking at the largest and smallest items, though not a breakthrough here.

Draw from Experience

A lot of the direction (augmenting / greedy) and the final details (prefix sums, wrap-around tricks) came from experience with many problems before.

Problem Transformation

Rewriting the formula in terms of pairs and cuts made the problem more tractable. Going all the way to max-cut was probably a distraction — I spent too long on a far more complicated framing — but the small transformations really did simplify things.

Problem Simplification

Not really noted above, but "what happens when a single person is flipped from ungifted to gifted" is itself a simplification — one local question instead of a global one.

Exploit the Constraints

The symmetry of the fixed-size 2D2D windows really simplifies the problem. We should have exploited it sooner (and in the end, somewhat did).

Learning points:

Topics: