cppAcademy
FROM LEARNING TO COMPETING

Proficient.
Put your skills to the test.

Bulgarian Informatics Group B · 2007–2026

274 independently explained problems: 54 Municipal, 54 Regional, 109 National Olympiad and 57 Autumn Tournament tasks. Difficulty labels are learning estimates, not official ratings. Some events share Junior/IATI or Group A/B problem sets. Special formats, scoring limitations and source clarifications are explained on affected tasks. The two previously featured Autumn problems keep their existing permanent URLs.
GROUP B2015 · MediumConstrained ballot-path dynamic programming

Count Increasing Two-Row Tables

Autumn Tournament (2015)

Submission name: table.cpp

Problem summary

Place1…2N once each into a2×N table, increasing along every row and column. Values in set A must be on top; values in B must be on the bottom; all other values are unrestricted. Count valid tables.

Input

N; then |A| and its values; then |B| and its values.

Output

The number of valid tables.

Constraints

2≤N≤35; A and B are disjoint, each of size at mostN.

Worked example

Original statement sample.

Input

4
2 3 2
3 4 8 7

Output

2

Solution analysis

  1. Process numbers1,2,…,2N in increasing order and decide only their row. Their order within a row is then forced. At every prefix, the number placed on top must be at least the number on the bottom: otherwise some bottom entry would have no smaller entry above it.
  2. Let dp[top] count choices after processing a prefix; bottom=processed−top. Put the next value on top if it is not forced into B and top<N. Put it below if it is not forced into A and bottom<top. This last inequality keeps every column increasing.
  3. After all2N numbers, read dp[N]. In the sample, the two top rows are1,2,3,5 and1,2,3,6; their complementary bottom rows are4,6,7,8 and4,5,7,8. Both obey the forced row choices.

Why it works

Choosing a row for every value gives exactly one row-sorted table. Its columns increase precisely when every processed prefix has at least as many top entries as bottom entries: the j-th bottom entry must appear after the j-th top entry. The DP enforces exactly this condition and all forced choices, and its alternatives are disjoint.

Complexity: O(N²) time and O(N) extra space:2N layers each have at mostN+1 states. The unconstrained maximum is Catalan(35)=3116285494907301262, which fits signed long long.

Watch out. A forced-bottom value may be impossible early in the sequence, giving zero ways. Do not multiply by row permutations: increasing rows determine their order uniquely.

Commented C++17 solution

An independently written complete program with standard headers.

#include<iostream>
#include<vector>
using namespace std;
int main(){
    int n;
    cin>>n;
    vector<int>forced(2*n+1,0);
    for(int row=1;row<=2;row++){
        int count;
        cin>>count;
        while(count--){
            int value;
            cin>>value;
            forced[value]=row;
        }
    }
    vector<long long>ways(n+1,0);
    ways[0]=1;
    for(int value=1;value<=2*n;value++){
        vector<long long>next(n+1,0);
        for(int top=0;top<=n;top++){
            int bottom=value-1-top;
            if(bottom<0||bottom>top){
                continue;
            }
            if(forced[value]!=2&&top<n){
                next[top+1]+=ways[top];
            }
            // A bottom cell needs an already placed smaller top cell.
            if(forced[value]!=1&&bottom<top){
                next[top]+=ways[top];
            }
        }
        ways.swap(next);
    }
    cout<<ways[n]<<'\n';
}

Source: Arena Olimpiici ↗