#include using namespace std; #define rep(i,a,b) for (auto i=(a); i<(b); ++i) #define iter(it,c) for (auto it = (c).begin(); it != (c).end(); ++it) typedef pair ii; typedef vector vi; typedef vector vii; typedef vector vvi; typedef long long ll; const int INF = 2147483647; const ll DINF = 1000000000000000000LL; struct node { int l, r; ll x, lazy; int mni; node() {} node(int _l, int _r) : l(_l), r(_r), x(DINF), lazy(0), mni(-1) { } node(int _l, int _r, int _x) : node(_l,_r) { x = _x; mni = _l; } node(node a, node b) : node(a.l,b.r) { if (a.x < b.x) { x = a.x; mni = a.mni; } else { x = b.x; mni = b.mni; } } void update(int v) { x = v; mni = l; } void range_update(int v) { lazy = v; } void apply() { x += lazy; lazy = 0; } void push(node &u) { u.lazy += lazy; } }; struct segment_tree { int n; vector arr; segment_tree() { } segment_tree(const vector &a) : n(size(a)), arr(4*n) { mk(a,0,0,n-1); } node mk(const vector &a, int i, int l, int r) { int m = (l+r)/2; return arr[i] = l > r ? node(l,r) : l == r ? node(l,r,a[l]) : node(mk(a,2*i+1,l,m),mk(a,2*i+2,m+1,r)); } node update(int at, ll v, int i=0) { propagate(i); int hl = arr[i].l, hr = arr[i].r; if (at < hl || hr < at) return arr[i]; if (hl == at && at == hr) { arr[i].update(v); return arr[i]; } return arr[i] = node(update(at,v,2*i+1),update(at,v,2*i+2)); } node query(int l, int r, int i=0) { propagate(i); int hl = arr[i].l, hr = arr[i].r; if (r < hl || hr < l) return node(hl,hr); if (l <= hl && hr <= r) return arr[i]; return node(query(l,r,2*i+1),query(l,r,2*i+2)); } node range_update(int l, int r, ll v, int i=0) { propagate(i); int hl = arr[i].l, hr = arr[i].r; if (r < hl || hr < l) return arr[i]; if (l <= hl && hr <= r) return arr[i].range_update(v), propagate(i), arr[i]; return arr[i] = node(range_update(l,r,v,2*i+1), range_update(l,r,v,2*i+2)); } void propagate(int i) { if (arr[i].l < arr[i].r) arr[i].push(arr[2*i+1]), arr[i].push(arr[2*i+2]); arr[i].apply(); } }; int main() { ios_base::sync_with_stdio(false); int n, w; cin >> n >> w; vector words(n); rep(i,0,n) { cin >> words[i]; } vi bestjmp; int lo = 1, hi = n, res = -1; while (lo <= hi) { int rows = (lo+hi)/2; segment_tree st(vector(n+1, DINF)); st.update(n, 0); vi jmp(n); stack S; S.push(ii(INF, n+1)); for (int at = n-1; at >= 0; at--) { int curw = (int)words[at].size(); while (curw >= S.top().first) { int val = S.top().first; int l = S.top().second; S.pop(); st.range_update(l, S.top().second-1, -val); } st.range_update(at+1, S.top().second-1, curw); S.push(ii(curw, at+1)); node nd = st.query(at+1, min(at+rows, n)); jmp[at] = nd.mni; st.update(at, 1 + nd.x); } if (st.query(0,0).x-1 <= w) { bestjmp.swap(jmp); res = rows; hi = rows - 1; } else { lo = rows + 1; } } vii loc(n); vi width(n); vector row(res); int at = 0, cols = 0; while (at < n) { cols++; int cnt = bestjmp[at]-at; rep(i,0,cnt) { loc[at+i] = ii(i, cols-1); row[i].push_back(at+i); width[cols-1] = max(width[cols-1], (int)words[at+i].size()); } rep(i,cnt,res) { row[i].push_back(-1); } at += cnt; } cout << res << " " << cols << endl; rep(i,0,cols) { cout << width[i] << " "; } cout << endl; rep(i,0,res) { rep(j,0,cols) { int len = 0; if (row[i][j] != -1) { cout << words[row[i][j]]; len += (int)words[row[i][j]].size(); } while (len != width[j]) { cout << " "; len++; } cout << " "; } cout << endl; } return 0; }