Counting Castles
We build "castles" on a w x h grid. A castle is defined by a row-by-row binary profile: each cell in the grid is either filled or empty, subject to the constraints that (1) the bottom row is comple...
Problem Statement
This archive keeps the full statement, math, and original media on the page.
We define a block to be a rectangle with a height of $1$ and an integer-valued length. Let a castle be a configuration of stacked blocks.
Given a game grid that is $w$ units wide and $h$ units tall, a castle is generated according to the following rules:
Blocks can be placed on top of other blocks as long as nothing sticks out past the edges or hangs out over open space.
All blocks are aligned/snapped to the grid.
Any two neighboring blocks on the same row have at least one unit of space between them.
The bottom row is occupied by a block of length $w$.
The maximum achieved height of the entire castle is exactly $h$.
The castle is made from an even number of blocks.
The following is a sample castle for $w=8$ and $h=5$:

Let $F(w,h)$ represent the number of valid castles, given grid parameters $w$ and $h$.
For example, $F(4,2) = 10$, $F(13,10) = 3729050610636$, $F(10,13) = 37959702514$, and $F(100,100) \bmod 1\,000\,000\,007 = 841913936$.
For example, $F(4,2) = 10$, $F(13,10) = 3729050610636$, $F(10,13) = 37959702514$, and $F(100,100) \bmod 1\,000\,000\,007 = 841913936$.
Problem 502: Counting Castles
Mathematical Foundation
Theorem 1 (Battlement Count without Height Restriction). The number of sequences with and for all is:
Proof. The first column admits choices. Each subsequent column may take any of the possible heights except the one chosen for the previous column, giving choices. By the multiplication principle, .
Theorem 2 (Inclusion-Exclusion for Maximum Height). The number of battlement profiles where at least one column reaches height is:
Proof. Let be the set of battlement profiles with heights in and the subset restricted to . Then and . The profiles with at least one column of height number .
Lemma 1 (Full Castle with Row-Profile DP). When the problem imposes additional structural constraints beyond simple battlement profiles (e.g., even/odd patterning of high and low columns, specific connectivity rules at interior cells), the count is computed via a transfer-matrix method. Define the state as the column profile (a binary vector of height indicating which cells are filled in a column), and let be the transfer matrix where if column profiles and may be adjacent. Then:
where encodes the valid starting profiles and the boundary conditions enforce the “at least one max-height column” constraint via inclusion-exclusion.
Proof. The transfer matrix encodes all valid local adjacencies. Raising it to the -th power and summing over valid initial and terminal states counts exactly the number of valid length- sequences. The inclusion-exclusion removes profiles that never reach height .
Editorial
Count castle-shaped profiles on a grid using profile dynamic programming. A castle profile is a sequence of column heights with battlement constraints. We simple battlement model with max-height constraint. We then simple model. Finally, transfer-matrix model (if needed).
Pseudocode
Simple battlement model with max-height constraint
If additional structural constraints apply, use transfer-matrix DP
Simple model:
Transfer-matrix model (if needed):
Enumerate valid column profiles (contiguous from bottom)
Build transfer matrix T of valid adjacencies
Compute T^(w-1) via matrix exponentiation mod p
Sum over valid start/end states
Subtract profiles that never reach height h
Complexity Analysis
- Time (simple battlement model): via modular exponentiation; space.
- Time (transfer-matrix model): for matrix exponentiation with states, or if states reduce to height values (giving states).
- Space: for the transfer matrix (or for full profile-based DP).
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD = 1e9 + 7;
ll power(ll base, ll exp, ll mod) {
ll result = 1;
base %= mod;
while (exp > 0) {
if (exp & 1) result = result * base % mod;
base = base * base % mod;
exp >>= 1;
}
return result;
}
// Simple battlement: no two adjacent columns same height, heights in [1, h]
ll simple_battlement(int w, int h) {
if (w == 0) return 0;
return (ll)h % MOD * power(h - 1, w - 1, MOD) % MOD;
}
// Full castle DP: battlement + at least one column reaches height h
// State: (last_height, reached_top)
ll full_castle(int w, int h) {
if (w == 0) return 0;
// dp[j][t]: count ending at height j, t=reached_top
vector<vector<ll>> dp(h + 1, vector<ll>(2, 0));
// Base: first column
for (int j = 1; j <= h; j++) {
int t = (j == h) ? 1 : 0;
dp[j][t] = 1;
}
for (int col = 2; col <= w; col++) {
// Compute totals for each t
ll total[2] = {0, 0};
for (int j = 1; j <= h; j++) {
for (int t = 0; t < 2; t++) {
total[t] = (total[t] + dp[j][t]) % MOD;
}
}
vector<vector<ll>> new_dp(h + 1, vector<ll>(2, 0));
for (int j2 = 1; j2 <= h; j2++) {
for (int t = 0; t < 2; t++) {
int t_new = (t == 1 || j2 == h) ? 1 : 0;
ll contrib = (total[t] - dp[j2][t] % MOD + MOD) % MOD;
new_dp[j2][t_new] = (new_dp[j2][t_new] + contrib) % MOD;
}
}
dp = new_dp;
}
ll result = 0;
for (int j = 1; j <= h; j++) {
result = (result + dp[j][1]) % MOD;
}
return result;
}
int main() {
// Test cases
printf("Simple battlement C(w, h):\n");
for (int w = 1; w <= 13; w++) {
printf(" C(%d, 10) = %lld\n", w, simple_battlement(w, 10));
}
printf("\nFull castle (must reach top):\n");
for (int w = 1; w <= 13; w++) {
printf(" C(%d, 10) = %lld\n", w, full_castle(w, 10));
}
printf("\nC(13, 10) = %lld\n", full_castle(13, 10));
return 0;
}
"""
Problem 502: Counting Castles
Count castle-shaped profiles on a grid using profile dynamic programming.
A castle profile is a sequence of column heights with battlement constraints.
"""
MOD = 10**9 + 7
def solve_simple_battlement(w: int, h: int):
"""Count profiles of width w, max height h, where no two adjacent columns
have the same height. Each column height in [1, h].
Result: h * (h-1)^(w-1)
"""
if w == 0:
return 0
return h * pow(h - 1, w - 1, MOD) % MOD
def solve_dp(w: int, h: int):
"""Profile DP for battlement counting.
dp[j] = number of valid profiles ending with height j.
"""
if w == 0:
return 0
dp = [1] * (h + 1) # dp[j] for j = 1..h, dp[0] unused
dp[0] = 0
for col in range(2, w + 1):
total = sum(dp) % MOD
new_dp = [0] * (h + 1)
for j in range(1, h + 1):
new_dp[j] = (total - dp[j]) % MOD
dp = new_dp
return sum(dp) % MOD
def solve_full_castle(w: int, h: int):
"""Full castle profile DP with additional constraints:
- Column heights in [1, h]
- No two adjacent columns have the same height (battlement)
- At least one column reaches height h (touches the top)
- The profile is connected (always true for contiguous columns from bottom)
Count = (profiles with no same-adjacent) - (profiles with no same-adjacent and all < h)
= h*(h-1)^(w-1) - (h-1)*(h-2)^(w-1)
"""
if w == 0:
return 0
if h == 1:
return 1 if w >= 1 else 0
# Total with max height h: no adjacent same
total = h * pow(h - 1, w - 1, MOD) % MOD
# Those not reaching height h: heights in [1, h-1], no adjacent same
without_top = (h - 1) * pow(h - 2, w - 1, MOD) % MOD if h >= 3 else (h - 1)
if h == 2 and w > 1:
without_top = 0 # only height 1, but w > 1 means all same -> 0 from battlement
result = (total - without_top) % MOD
return result
def solve_profile_dp_full(w: int, h: int):
"""Full profile DP tracking whether max height h has been reached.
State: (last_height, reached_top)
"""
if w == 0:
return 0
# dp[j][t] = count of profiles of current length ending at height j,
# where t=1 means at least one column reached height h
dp = {}
for j in range(1, h + 1):
t = 1 if j == h else 0
dp[(j, t)] = 1
for col in range(2, w + 1):
new_dp = {}
# Compute total for each 'reached_top' value
total = [0, 0]
for (j, t), cnt in dp.items():
total[t] = (total[t] + cnt) % MOD
for j2 in range(1, h + 1):
for t2 in range(2):
# t_new: if t2=1 (already reached) or j2=h
t_new = 1 if (t2 == 1 or j2 == h) else 0
# Sum over all j != j2 with reached_top = t2
# = total[t2] - dp.get((j2, t2), 0)
contrib = (total[t2] - dp.get((j2, t2), 0)) % MOD
key = (j2, t_new)
new_dp[key] = (new_dp.get(key, 0) + contrib) % MOD
dp = new_dp
# Sum all states with reached_top = 1
result = 0
for (j, t), cnt in dp.items():
if t == 1:
result = (result + cnt) % MOD
return result
# Compute results
print("Counting Castles - Profile DP Results:")
print()
print("Simple battlement (no adjacent same heights):")
for w in range(1, 15):
for h in [5, 10]:
c = solve_simple_battlement(w, h)
print(f" C({w}, {h}) = {c}")
print()
print("Full castle (must reach top, no adjacent same):")
for w in range(1, 15):
for h in [5, 10]:
c = solve_profile_dp_full(w, h)
print(f" C({w}, {h}) = {c}")
print()
print("Specific case C(13, 10):")
print(f" Simple: {solve_simple_battlement(13, 10)}")
print(f" Must reach top: {solve_profile_dp_full(13, 10)}")