diff --git a/content/5_Plat/DC-DP.mdx b/content/5_Plat/DC-DP.mdx
index d056f8874b..1260e872ac 100644
--- a/content/5_Plat/DC-DP.mdx
+++ b/content/5_Plat/DC-DP.mdx
@@ -8,9 +8,7 @@ prerequisites:
- convex-hull-trick
---
-Allows you to reduce $\mathcal{O}(N^2)$ to $\mathcal{O}(N\log N)$.
-
-## Tutorial
+## Overview
-## Example - Circular Barn
+Consider a dynamic programming problem with the following formula
-
+$$
+dp(i,j) = \min_{0\leq k \leq j} ( dp(i-1, k-1) + C(k,j)),
+$$
-You should already be familiar with the [CHT solution](/plat/convex-hull-trick#problems).
+where $C(i,j)$ is a cost function and you can compute it in $O(1)$ time.
+Furthermore, $dp(i,j) =0$ for $j<0$.
+
+The straightforward implementation gives a runtime of $O(MN^2)$ if $0\leq i < M$ and $0\leq j < N$.
+Divide & Conquer DP allows this to be optimized to $O(M N \log N)$.
+
+For each $i,j$, let $\text{opt}(i,j)$ be the value of $k$ that minimizes the right hand side of the equation.
+Divide & Conquer DP **only applies if**
+
+$$
+\text{opt}(i,j) \leq \text{opt}(i,j+1).
+$$
-
+Often, proving this with the given cost function is challenging,
+but if the cost function satisfies the [quadrangle inequality](https://codeforces.com/blog/entry/86306), the condition holds.
-Add analysis later.
+We can then apply the idea behind Divide & Conquer.
+Fix a given $i$. First, compute $\text{opt}(i,n/2)$.
+Then compute $\text{opt}(i, n/4)$ using the fact that it is less than or equal to $\text{opt}(i, n/2)$.
+Similarly, we can compute $\text{opt}(i, 3n/4)$ and recursively split the ranges in half, keeping track on the lower and upper bounds.
-
+Since each possible value of $\text{opt}(i, j)$ appears $O(\log n)$ times, this gives a final runtime of $O(mn \log n)$.
+
+## Example - Circular Barn
+
+
+
+You should already be familiar with the [CHT solution](/plat/convex-hull-trick#problems).
-Check the
-[official editorial](http://www.usaco.org/current/data/sol_cbarn_platinum_feb16.html).
+### Explanation
+
+We iterate through the possibilities of the location of the first door.
+For each of the first doors, we can now view the barn linearly.
+All further calculations will be done assuming the barn is a linear sequence of doors starting at the first opened door.
+
+Let $dp(i,k)$ denote the location of the last door if we place $k$ doors optimally among the first $i$ rooms.
+The idea is that $dp(i,k) \leq dp(i+1, k)$.
+Assume this was not true for the sake of contradiction.
+Then $dp(i,k) > dp(i+1,k)$, so we also have $dp(i+1,k) \leq i$.
+But then we could have used the best possible setup for $(i+1,k)$ in the $(i,k)$ setup as well, since all open doors are among the first $i$ rooms anyways.
+
+Since the monotonicity condition is held, we can now perform Divide & Conquer DP.
+Fix the value of $k$ and compute $dp(n/2, k)$.
+Then compute it for the left and right halves of the array.
+
+### Implementation
+
+**Time Complexity:** $\mathcal{O}(n^2 k \log n)$, since we need to check $n$ rooms for the optimal first barn position.
+
+
+
+
+```cpp
+#include
+using namespace std;
+#define ll long long
+
+const int MAX_N = 1000;
+const int MAX_K = 7;
+
+// calc[i][j] stores the # of steps to get all cows distance j away to door i
+// to make implementing a cyclic array easier, we double the size
+vector> calc(2 * MAX_N, vector(MAX_N + 1, 0));
+// dp[i][j] stores the answer for doors 0,1,.., j and i doors open
+vector> dp(MAX_K + 1, vector(MAX_N + 1, INT64_MAX));
+
+int rot;
+
+void compdp(int k, int begin, int end, int rl, int rr) {
+ // fixed k, begin and end are the ends of the array, rl and rr are the
+ // bounds on the last door used
+ int mid = (begin + end) / 2;
+
+ ll best = INT64_MAX;
+ int best_last = -1;
+ // best is min amount moved, last is the last door used
+ for (int last = rl; last <= min(mid, rr); last++) {
+ ll cost = dp[k - 1][last - 1] + calc[last + rot][mid - last + 1];
+ if (cost < best) {
+ best = cost;
+ best_last = last;
+ } else if (cost == best && last < best_last) {
+ best_last = last;
+ }
+ }
+ dp[k][mid] = best;
+ if (begin == end) { return; }
+ compdp(k, begin, mid, rl, best_last);
+ compdp(k, mid + 1, end, best_last, rr);
+}
+
+int main() {
+ freopen("cbarn.in", "r", stdin);
+ int n, k;
+ cin >> n >> k;
+
+ vector a(2 * n);
+ for (int i = 0; i < n; i++) {
+ cin >> a[i];
+ a[i + n] = a[i];
+ }
+
+ for (int i = 0; i < n; i++) {
+ for (int j = 1; j <= n; j++) {
+ calc[i][j] = calc[i + n][j] =
+ calc[i][j - 1] + (ll)a[i + j - 1] * (j - 1);
+ }
+ }
+
+ // rotate stores where we start in the linear representation
+ ll ans = INT64_MAX;
+ for (rot = 0; rot < n; rot++) {
+ for (int i = 0; i < n; i++) { dp[0][i] = calc[rot][i + 1]; }
+ for (int i = 1; i < k; i++) {
+ for (int j = 0; j < n; j++) { dp[i][j] = INT64_MAX; }
+ compdp(i, i, n - 1, i, n - 1);
+ }
+ ans = min(ans, dp[k - 1][n - 1]);
+ }
+
+ freopen("cbarn.out", "w", stdout);
+ cout << ans << endl;
+}
+```
+
+
+
## Problems
@@ -54,6 +171,8 @@ that you swap do not count towards the minimum number of bubble sort swaps.