Ideal City
A city consists of N unit-square blocks on a grid (connected via shared edges). The distance between two blocks is the shortest path length through adjacent blocks. Compute the sum of pairwise shortest-path distances...
Problem Statement
Rendered from the "Problem Summary" section in the LaTeX write-up.
A city consists of $N$ unit-square blocks on a grid (connected via shared edges). The distance between two blocks is the shortest path length through adjacent blocks. Compute the sum of pairwise shortest-path distances modulo $10^9$.
Editorial
The solution write-up is rendered from the LaTeX source, with equations kept live through MathJax.
Solution
Decomposition into Horizontal and Vertical Components
The shortest path distance between any two blocks in a connected grid equals the Manhattan distance (the grid has no ``holes'' blocking shortest paths between adjacent connected components). The key structural insight is:
Theorem.
The sum of pairwise distances decomposes as: \[ \sum_{i < j} d(i, j) = \sum_{i < j} |x_i - x_j|_{\mathrm{eff}} + \sum_{i < j} |y_i - y_j|_{\mathrm{eff}} \] where the effective horizontal and vertical distances are computed via segment trees.
Horizontal segments: Group blocks into maximal horizontal runs (consecutive blocks in the same row). Build a tree where two segments are adjacent if they overlap in column range and lie in consecutive rows. The horizontal contribution to the distance between two blocks equals the tree distance between their respective horizontal segments.
Vertical segments: Analogously, group blocks into maximal vertical runs and build a vertical segment tree.
Computing the Tree Contribution
For a tree with $n$ nodes where node $i$ has weight $w_i$ (the number of blocks in that segment), the sum of weighted pairwise distances is: \[ \sum_{\text{edge } (u,v)} w_u^{\text{sub}} \cdot (W - w_u^{\text{sub}}) \] where $w_u^{\text{sub}}$ is the total weight of $u$'s subtree and $W = \sum w_i$. Each edge is counted once, contributing the product of the weights on its two sides.
Complexity
Time: $O(N \log N)$ for sorting and building segment adjacency.
Space: $O(N)$.
Code
C++ solution used for this page.
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1000000000;
// Compute sum of pairwise distances in a tree where each node has weight w[i]
// (number of blocks in the segment). Sum of distances weighted by w.
long long treePairwiseDist(vector<vector<int>> &adj, vector<int> &w, int n){
if(n == 0) return 0;
// Root at 0, compute subtree weight sums
vector<int> subtreeW(n, 0);
vector<int> parent(n, -1);
vector<int> order;
// BFS
vector<bool> visited(n, false);
queue<int> q;
q.push(0); visited[0] = true;
while(!q.empty()){
int u = q.front(); q.pop();
order.push_back(u);
for(int v : adj[u]){
if(!visited[v]){
visited[v] = true;
parent[v] = u;
q.push(v);
}
}
}
// Compute subtree weights in reverse BFS order
for(int i = 0; i < n; i++) subtreeW[i] = w[i];
for(int i = n - 1; i >= 1; i--){
subtreeW[parent[order[i]]] += subtreeW[order[i]];
}
int totalW = subtreeW[0];
long long result = 0;
// Each edge (parent[v], v) separates subtreeW[v] and totalW - subtreeW[v]
for(int i = 1; i < n; i++){
int v = order[i];
long long a = subtreeW[v];
long long b = totalW - a;
result = (result + a * b) % MOD;
}
return result;
}
long long solve(vector<pair<int,int>> &blocks, bool horizontal){
int N = (int)blocks.size();
// Sort blocks
vector<pair<int,int>> sorted_blocks = blocks;
if(horizontal){
// Group by row (y), then by x
sort(sorted_blocks.begin(), sorted_blocks.end(),
[](const pair<int,int> &a, const pair<int,int> &b){
if(a.second != b.second) return a.second < b.second;
return a.first < b.first;
});
} else {
// Group by column (x), then by y
sort(sorted_blocks.begin(), sorted_blocks.end(),
[](const pair<int,int> &a, const pair<int,int> &b){
if(a.first != b.first) return a.first < b.first;
return a.second < b.second;
});
}
// Build segments: maximal consecutive runs
struct Segment {
int fixedCoord; // row or column
int start, end; // range in the other coordinate
int weight; // number of blocks
};
vector<Segment> segments;
// Assign segment id to each block
map<pair<int,int>, int> blockToSeg;
int i = 0;
while(i < N){
int j = i;
if(horizontal){
int row = sorted_blocks[i].second;
while(j < N && sorted_blocks[j].second == row &&
(j == i || sorted_blocks[j].first == sorted_blocks[j-1].first + 1)){
j++;
}
int segId = (int)segments.size();
segments.push_back({row, sorted_blocks[i].first, sorted_blocks[j-1].first, j - i});
for(int k = i; k < j; k++){
blockToSeg[sorted_blocks[k]] = segId;
}
} else {
int col = sorted_blocks[i].first;
while(j < N && sorted_blocks[j].first == col &&
(j == i || sorted_blocks[j].second == sorted_blocks[j-1].second + 1)){
j++;
}
int segId = (int)segments.size();
segments.push_back({col, sorted_blocks[i].second, sorted_blocks[j-1].second, j - i});
for(int k = i; k < j; k++){
blockToSeg[sorted_blocks[k]] = segId;
}
}
i = j;
}
int nSeg = (int)segments.size();
// Build adjacency between segments
vector<vector<int>> adj(nSeg);
set<pair<int,int>> edgeSet;
// Two segments are adjacent if they are in consecutive rows/columns
// and share at least one coordinate in the other dimension
set<pair<int,int>> blockSet(blocks.begin(), blocks.end());
for(auto &[pos, segId] : blockToSeg){
pair<int,int> neighbor;
if(horizontal){
neighbor = {pos.first, pos.second + 1}; // block above
} else {
neighbor = {pos.first + 1, pos.second}; // block to the right
}
if(blockToSeg.count(neighbor)){
int neighborSeg = blockToSeg[neighbor];
if(neighborSeg != segId){
auto edge = make_pair(min(segId, neighborSeg), max(segId, neighborSeg));
if(!edgeSet.count(edge)){
edgeSet.insert(edge);
adj[segId].push_back(neighborSeg);
adj[neighborSeg].push_back(segId);
}
}
}
}
// Compute sum of pairwise distances in segment tree
vector<int> weights(nSeg);
for(int s = 0; s < nSeg; s++) weights[s] = segments[s].weight;
return treePairwiseDist(adj, weights, nSeg);
}
int DistanceSum(int N, int *X, int *Y){
vector<pair<int,int>> blocks(N);
for(int i = 0; i < N; i++){
blocks[i] = {X[i], Y[i]};
}
long long hDist = solve(blocks, true); // horizontal segments
long long vDist = solve(blocks, false); // vertical segments
return (int)((hDist + vDist) % MOD);
}
int main(){
int N;
cin >> N;
int *X = new int[N], *Y = new int[N];
for(int i = 0; i < N; i++) cin >> X[i] >> Y[i];
cout << DistanceSum(N, X, Y) << "\n";
delete[] X; delete[] Y;
return 0;
}
Source Files and Assets
Raw files are still available here when you want the original TeX, C++, or statement assets.