Reconstruct the Queue
Autumn Tournament (2021)
Submission name: queue.cpp
Problem summary
A queue initially has at least two students. In each round newcomers enter the gaps starting with the last gap, then skipping K gaps before choosing the next gap towards the front, continuing wherever possible. Given the final length N, find the smallest possible initial length.
Input
N and K.
Output
The minimum possible initial queue length.
Constraints
1 < N < 10^16; 0 ≤ K < 100.
Worked example
Original statement sample.
Input
10 1
Output
5
Solution analysis
- A queue of x students has x−1 gaps. Selecting every (K+1)-st gap from the back admits ceil((x−1)/(K+1)) newcomers. Thus a round deterministically changes x to F(x)=x+(x+K−1)/(K+1), using integer division.
- F is strictly increasing, since x increases and the number of newcomers never decreases. Therefore a final length y has at most one predecessor. Binary-search x in [2,y−1] for the first F(x)≥y. If equality holds, replace y by x and repeat; if not, y cannot result from another round and is the smallest starting length.
- For N=10,K=1, F(7)=7+ceil(6/2)=10, and F(5)=5+ceil(4/2)=7. There is no x with F(x)=5, so the answer is 5. For N=8,K=0, every F(x)=2x−1 is odd; no round can end at 8 and its initial length must already be 8.
Why it works
Strict monotonicity makes each reverse step unique. Every valid earlier starting length must follow this unique predecessor chain. Continuing until the chain has no predecessor therefore returns its smallest attainable initial length.
Watch out. Do not assume an inverse exists for every length. K=0 creates only odd next lengths. Use 64-bit arithmetic; N can exceed a 32-bit integer.
Commented C++17 solution
An independently written complete program with standard headers.
#include<iostream>
using namespace std;
int main(){
long long length;
int skipped;
cin>>length>>skipped;
auto afterRound=[&](long long previous){
return previous+(previous+skipped-1)/(skipped+1);
};
while(length>2){
long long low=2,high=length-1;
while(low<high){
long long middle=low+(high-low)/2;
if(afterRound(middle)>=length){
high=middle;
}
else{
low=middle+1;
}
}
if(afterRound(low)!=length){
break;
}
// The increasing transition has at most one predecessor.
length=low;
}
cout<<length<<'\n';
}