#include using namespace std; constexpr int INF = 1e9; struct RMQ { int N, logN; vector> V; RMQ(const vector& vec) : N(vec.size()) { for (logN = 0; (1<(N, -INF)); copy(vec.begin(), vec.end(), V[0].begin()); for (int i = 0; i < logN; ++i) { for (int j = 0; j < N; ++j) { int m = j + (1<= r) return -INF; int p = 0; while (l + (1<<(p+1)) < r) ++p; return max(V.at(p)[l], V.at(p)[r - (1<> test(int L, int N, const RMQ& rmq) { vector dp(N, +INF); vector par(N, -1); for (int i = 0; i < N; ++i) { // For i=0..L-1 it is clearly optimal to keep everything in a single column. if (i < L) { dp[i] = rmq.query(0, i+1); par[i] = -1; } else { // Option 1: We can fill the column up entirely. dp[i] = dp[i - L] + 1 + rmq.query(i-L+1, i+1); par[i] = i - L; } // Discard points we don't care about } return {dp[N-1], par}; } int main() { int N, W; cin >> N >> W; vector S(N); vector wlengths; for (auto& s : S) cin >> s, wlengths.push_back(s.length()); RMQ rmq(wlengths); // We will binary search over L, the maximum number of lines we can use per // column. Define dp as follows: // dp[i] is the minimum width to store words [0..i] in some columns of // height at most L. Obviously we intend to then return dp[N-1]. Then: // dp[i] = min_{1 \leq j \leq L} dp[i - j] + w(i-j+1 .. i) + 1 // where w(a .. b) is the maximum width among words [a .. b]. // We maintain a set of values { dp[i-j] + w(i-j+1 .. i) } and simply // assign dp[i] to be the minimum of this set. // Note that as a function of j, w(i-j+1 .. i) is staircase shaped, and // since dp[i-j] is monotonically decreasing in j, we want to check only // those values where the maximum width is about to change, i.e. we want // those j such that w(i-j+1 .. i) != w(i-j .. i). So we maintain these // staircase points and discard those superseded by w(i+1). We also need // to check j = L separately. int l = 1, r = N; while (l < r) { int m = (l + r) / 2, w_for_m; std::tie(w_for_m, std::ignore) = test(m, N, rmq); if (w_for_m <= W) r = m; else l = m + 1; } int rows = l; int w; vector par; std::tie(w, par) = test(rows, N, rmq); // vv output/formatting section vv vector colpos, colend, widths; int cur = N-1; while (cur >= 0) { colpos.push_back(par[cur]+1); colend.push_back(cur); widths.push_back(rmq.query(par[cur]+1, cur+1)); cur = par[cur]; } reverse(colpos.begin(), colpos.end()); reverse(colend.begin(), colend.end()); reverse(widths.begin(), widths.end()); cout << rows << ' ' << colpos.size() << endl; for (size_t i = 0; i < widths.size(); ++i) cout << widths[i] << " \n"[i+1 == widths.size()]; for (int row = 0; row < rows; ++row) { for (size_t c = 0; c < colpos.size(); ++c) { int spaces = widths[c]; if (colpos[c] <= colend[c]) cout << S[colpos[c]], spaces -= (int)S[colpos[c]].size(); if (spaces) cout << string(spaces, ' '); ++colpos[c]; cout << " \n"[c+1 == colpos.size()]; } } return 0; }