cppAcademy
FROM LEARNING TO COMPETING

Proficient.
Put your skills to the test.

Bulgarian Informatics Group B · 2009–2026

217 independently explained problems: 54 Municipal, 54 Regional and 109 National. Start with the easier tasks and work upwards. Difficulty labels are learning estimates, not official ratings; Group B also includes challenging national-level problems. National-round tasks shared by Groups A and B are included. Years follow Arena’s competition-season labels. Source clarifications and special grader requirements are noted on affected tasks.
GROUP AB2019 · HardTwo-dimensional range maximum

Longest Mountain Itinerary

National Olympiad — National round (2019 season)

Submission name: theclimb.cpp

Problem summary

Choose a longest sequence of grid cells with strictly increasing altitude, with Manhattan distance at most D between consecutive cells. The required answer follows the official example and counts the cells in the itinerary.

Source clarification. The prose asks for days, but its described fifteen-cell itinerary is reported as 15, rather than fourteen transitions. This solution follows that demonstrated counting convention.

Input

N M D, then N rows of M altitudes.

Output

The maximum itinerary length, as in the official example.

Constraints

1 ≤ N,M,D ≤ 500; 1 ≤ altitude ≤ 1,000,000.

Worked example

An independently prepared example for this explanation.

Input

4 5 3
39 13 26 20 17
37 17 14 22 24
42 12 10 21 33
18 20 13 19 31

Output

15

Solution analysis

  1. Process cells by increasing altitude. The best itinerary ending at a cell is one plus the maximum previously computed value within Manhattan distance D. All cells of an equal altitude must be queried before any of them is inserted, preserving strict increase.
  2. Transform (r,c) into (u,v)=(r+c,r−c+M−1). Manhattan distance in the original grid equals max(|Δu|,|Δv|), so the allowed predecessor region is an axis-aligned square. An iterative two-dimensional segment tree supports point maximum updates and rectangle maximum queries.
  3. The official example describes fifteen visited altitudes from 10 to 42 and outputs 15. A single cell has value 1, so the code consistently counts cells. At height 13 both equal-height candidates see only lower altitudes and cannot extend one another.

Why it works

The coordinate identity converts exactly the permitted predecessor cells into the queried rectangle. Induct on increasing distinct altitude: every legal predecessor has already been inserted and every inserted cell has lower altitude. Therefore the rectangle maximum gives the longest legal predecessor itinerary, and appending the current cell yields the optimum for that endpoint. Equal-altitude batching prevents invalid transitions. Maximizing endpoint values gives the longest itinerary.

Complexity: O(NM log(NM)+NM log²(N+M)) time and O((N+M)²+NM) space. The tree uses fewer than 4.2 million int entries at the maximum dimensions.

Watch out. Batch equal altitudes. The rotated grid contains unused parity positions; leaving them zero is correct. Count cells to match the official sample, despite its wording about days.

Commented C++17 solution

An independently written complete program with standard headers.

#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
struct Cell{
    int altitude,u,v,value;
};
class RangeMaximum{
    int size;
    int stride;
    vector<int>tree;
    int&at(int x,int y){
        return tree[x*stride+y];
    }
    int queryRow(int x,int left,int right){
        int answer=0;
        for(left+=size,right+=size;left<=right;left/=2,right/=2){
            if(left%2){
                answer=max(answer,at(x,left++));
            }
            if(right%2==0){
                answer=max(answer,at(x,right--));
            }
        }
        return answer;
    }
    public:
    explicit RangeMaximum(int bound):size(1){
        while(size<bound){
            size*=2;
        }
        stride=2*size;
        tree.assign(stride*stride,0);
    }
    void update(int x,int y,int value){
        x+=size;
        y+=size;
        at(x,y)=max(at(x,y),value);
        for(int column=y/2;column;column/=2){
            at(x,column)=max(at(x,2*column),at(x,2*column+1));
        }
        for(x/=2;x;x/=2){
            for(int column=y;column;column/=2){
                at(x,column)=max(at(2*x,column),at(2*x+1,column));
            }
        }
    }
    int query(int x1,int x2,int y1,int y2){
        x1=max(x1,0);
        y1=max(y1,0);
        x2=min(x2,size-1);
        y2=min(y2,size-1);
        int answer=0;
        for(x1+=size,x2+=size;x1<=x2;x1/=2,x2/=2){
            if(x1%2){
                answer=max(answer,queryRow(x1++,y1,y2));
            }
            if(x2%2==0){
                answer=max(answer,queryRow(x2--,y1,y2));
            }
        }
        return answer;
    }
};
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n,m,distance;
    cin>>n>>m>>distance;
    vector<Cell>cells;
    for(int r=0;r<n;r++){
        for(int c=0;c<m;c++){
            int altitude;
            cin>>altitude;
            cells.push_back({altitude,r+c,r-c+m-1,0});
        }
    }
    sort(cells.begin(),cells.end(),[](const Cell&a,const Cell&b){
        return a.altitude<b.altitude;
    });
    RangeMaximum maximum(n+m-1);
    int answer=0;
    for(int begin=0;begin<static_cast<int>(cells.size());){
        int end=begin;
        while(end<static_cast<int>(cells.size())&&cells[end].altitude==cells[begin].altitude){
            Cell&cell=cells[end];
            cell.value=
                1+maximum.query(cell.u-distance,cell.u+distance,cell.v-distance,cell.v+distance);
            answer=max(answer,cell.value);
            end++;
        }
        // Insert only after every cell of this height has been evaluated.
        for(int i=begin;i<end;i++){
            maximum.update(cells[i].u,cells[i].v,cells[i].value);
        }
        begin=end;
    }
    cout<<answer<<'\n';
}

Source: Arena Olimpiici ↗