#include #include #include #include #include #include #include #include #pragma GCC optimize ("O3") #pragma GCC target ("sse4") typedef long long llong; const int MAXN = 1000000 + 10; const int MAXN3 = 100000 + 10; const int MAXN2 = 11; const short MAXB = (1 << 10); const llong INF = 1e18; const int INTINF = 1e9; const unsigned short BUFF_SIZE = (1 << 15); char buff[BUFF_SIZE]; unsigned short buffPos = BUFF_SIZE-1; inline void readChar() { if (++buffPos == BUFF_SIZE) fread(buff, BUFF_SIZE, 1, stdin), buffPos = 0; } inline void readInt(int &num) { num = 0; for (; '0' > buff[buffPos] || buff[buffPos] > '9' ; readChar()); for (; '0' <= buff[buffPos] && buff[buffPos] <= '9' ; readChar()) { num = 10*num + buff[buffPos]-'0'; } } inline void readLong(llong &num) { num = 0; for (; '0' > buff[buffPos] || buff[buffPos] > '9' ; readChar()); for (; '0' <= buff[buffPos] && buff[buffPos] <= '9' ; readChar()) { num = 10*num + buff[buffPos]-'0'; } } int n; llong b; struct Stick { int h; llong p; int idx; }; Stick s[MAXN]; Stick inputS[MAXN3]; std::vector indices2[MAXN3]; std::vector indices[MAXN]; std::vector sol[MAXN]; int solGroups; llong cost2(int count) { std::multiset set; for (int i = 1 ; i <= count ; ++i) { set.insert(b); } llong res = 1LL * count * count * count; for (int i = 1 ; i <= n ; ++i) { if (set.empty()) { return INF; } auto it = set.lower_bound(s[i].h); auto val = *it; if (it != set.end()) { if (val != s[i].h) set.insert(val - s[i].h); set.erase(set.find(val)); } else { set.erase(set.begin()); res += s[i].p; } } return res; } void getSol2(int count) { std::multiset > set; for (int i = 1 ; i <= count ; ++i) { set.insert({b, i}); } for (int i = 1 ; i <= n ; ++i) { if (set.empty()) { assert(false); } auto it = set.lower_bound({s[i].h, 0}); auto val = *it; if (it != set.end()) { indices2[val.second].push_back(s[i].idx); if (val.first != s[i].h) set.insert({val.first - s[i].h, val.second}); set.erase(set.find(val)); } else { indices2[set.begin()->second].push_back(s[i].idx); set.erase(set.begin()); } } } void printRealSol() { std::cout << solGroups << '\n'; for (int i = 1 ; i <= solGroups ; ++i) { std::cout << sol[i].size(); for (int &idx : sol[i]) { std::cout << ' ' << idx; } std::cout << '\n'; } } std::pair solve2() { std::sort(s + 1, s + 1 + n, [&](auto x, auto y) { return x.p > y.p || (x.p == y.p && x.h > y.h); }); // std::cout << "sorted\n"; // for (int i = 1 ; i <= n ; ++i) // { // } int l = 0, r = n, mid; while (l < r - 1) { mid = (l + r) / 2; llong left = cost2(mid); llong right = cost2(mid + 1); if (left - right >= 0) l = mid; else r = mid; } // std::cout << "r is: " << r << '\n'; // std::cout << "costs\n"; // for (int i = 1 ; i <= n ; ++i) // { // std::cout << cost(i) << '\n'; // } // std::cout << "diffs\n"; // for (int i = 1 ; i < n ; ++i) // { // std::cout << cost(i) - cost(i + 1) << '\n'; // } // fflush(stdout); getSol2(r); // printSol(r); return {cost2(r), r}; } llong cost(int count) { std::priority_queue pq; std::stack leftovers; for (int i = 1 ; i <= count ; ++i) { pq.push(b - 1); } llong res = 1LL * count * count * count; for (int i = count + 1 ; i <= n ; ++i) { while (pq.size() && pq.top() >= s[i].h) { leftovers.push(pq.top()); pq.pop(); } if (leftovers.size()) { assert(leftovers.top() >= s[i].h); pq.push(leftovers.top() - s[i].h); leftovers.pop(); } else { return INF; } } while (leftovers.size()) { pq.push(leftovers.top()); leftovers.pop(); } int cntPopped = 0; for (int i = 1 ; i <= count ; ++i) { while (pq.size() > cntPopped && pq.top() >= s[i].h - 1) { leftovers.push(pq.top()); pq.pop(); } assert(pq.size() >= cntPopped); if (pq.size() == cntPopped) { while (pq.size()) { pq.pop(); } cntPopped = 0; } if (leftovers.size()) { if (leftovers.top() + 1 != s[i].h) { pq.push(leftovers.top() - s[i].h); } leftovers.pop(); } else { cntPopped++; res += s[i].p; } } return res; } void getSol(int count) { std::multiset > set; for (int i = 1 ; i <= count ; ++i) { set.insert({b - 1, i}); } for (int i = count + 1 ; i <= n ; ++i) { if (set.empty()) { assert(false); } auto it = set.lower_bound({s[i].h, 0}); auto val = *it; if (it != set.end()) { indices[val.second].push_back(s[i].idx); set.insert({val.first - s[i].h, val.second}); set.erase(set.find(val)); } else { assert(false); } } for (int i = 1 ; i <= count ; ++i) { if (set.empty()) { assert(false); } auto it = set.lower_bound({s[i].h - 1, 0}); auto val = *it; if (it != set.end()) { indices[val.second].push_back(s[i].idx); if (val.first + 1 != s[i].h) set.insert({val.first - s[i].h, val.second}); set.erase(set.find(val)); } else { indices[set.begin()->second].push_back(s[i].idx); set.erase(set.begin()); } } } std::pair solve() { std::sort(s + 1, s + 1 + n, [&](auto x, auto y) { return x.h > y.h || (x.h == y.h && x.p < y.p); }); // std::cout << "sorted\n"; // for (int i = 1 ; i <= n ; ++i) // { // } int l = 0, r = n, mid; while (l < r - 1) { if (n <= 100000) { mid = (l + r) / 2; llong left = cost(mid); llong right = cost(mid + 1); if (left - right >= 0) l = mid; else r = mid; } else { mid = (l + r) / 2; llong left = cost(mid); if (left == INF) l = mid; else r = mid; } } // std::cout << "r is: " << r << '\n'; // std::cout << "costs\n"; // for (int i = 1 ; i <= n ; ++i) // { // std::cout << cost(i) << '\n'; // } // std::cout << "diffs\n"; // for (int i = 1 ; i < n ; ++i) // { // std::cout << cost(i) - cost(i + 1) << '\n'; // } // fflush(stdout); getSol(r); // printSol(r); return {cost(r), r}; } llong dp[MAXB][MAXB][MAXN2]; int idx[MAXB][MAXB][MAXN2]; llong f(int maskChosen, int maskLast, int groupCnt) { if (maskChosen == (1 << n) - 1) { if (maskLast == 0) groupCnt--; return groupCnt * groupCnt * groupCnt; } if (idx[maskChosen][maskLast][groupCnt] != 0) { return dp[maskChosen][maskLast][groupCnt]; } llong sum = 0; llong pen = 0; for (int i = 1 ; i <= n ; ++i) { if (maskLast & (1 << (i - 1))) { sum += s[i].h; pen = s[i].p; } } dp[maskChosen][maskLast][groupCnt] = INF; for (int i = 1 ; i <= n ; ++i) { if (maskChosen & (1 << (i - 1))) { continue; } if (f(maskChosen | (1 << (i - 1)), (1 << (i - 1)), groupCnt + 1) + (s[i].h > b ? s[i].p : 0) < dp[maskChosen][maskLast][groupCnt]) { dp[maskChosen][maskLast][groupCnt] = f(maskChosen | (1 << (i - 1)), (1 << (i - 1)), groupCnt + 1) + (s[i].h > b ? s[i].p : 0); idx[maskChosen][maskLast][groupCnt] = -i; } if (sum >= b) { continue; } if (sum + s[i].h <= b && f(maskChosen | (1 << (i - 1)), maskLast | (1 << (i - 1)), groupCnt) < dp[maskChosen][maskLast][groupCnt]) { dp[maskChosen][maskLast][groupCnt] = f(maskChosen | (1 << (i - 1)), maskLast | (1 << (i - 1)), groupCnt); idx[maskChosen][maskLast][groupCnt] = i; } if (f(maskChosen | (1 << (i - 1)), 0, groupCnt + 1) + s[i].p < dp[maskChosen][maskLast][groupCnt]) { dp[maskChosen][maskLast][groupCnt] = f(maskChosen | (1 << (i - 1)), 0, groupCnt + 1) + s[i].p; idx[maskChosen][maskLast][groupCnt] = n + i; } } return dp[maskChosen][maskLast][groupCnt]; } void solveExp() { llong sum = 0; llong res = 0; std::cerr << f(0, 0, 1) << '\n'; int maskChosen = 0; int maskLast = 0; int groupCnt = 1; while (maskChosen != (1 << n) - 1) { assert(idx[maskChosen][maskLast] != 0); if (idx[maskChosen][maskLast][groupCnt] < 0) { groupCnt++; int i = -idx[maskChosen][maskLast][groupCnt]; indices[groupCnt].push_back(i); res += (s[i].h > b ? s[i].p : 0); maskChosen |= (1 << (i - 1)); maskLast = (1 << (i - 1)); sum = s[i].h; continue; } assert(sum < b); if (idx[maskChosen][maskLast][groupCnt] <= n) { int i = idx[maskChosen][maskLast][groupCnt]; indices[groupCnt].push_back(i); assert(sum + s[i].h <= b); sum += s[i].h; maskChosen |= (1 << (i - 1)); maskLast |= (1 << (i - 1)); } else { int i = idx[maskChosen][maskLast][groupCnt] - n; indices[groupCnt].push_back(i); groupCnt++; res += s[i].p; maskChosen |= (1 << (i - 1)); maskLast = 0; assert(sum + s[i].h > b); sum = 0; } } groupCnt -= (maskLast == 0); res += 1LL * groupCnt * groupCnt * groupCnt; std::cerr << res << '\n'; std::cout << groupCnt << '\n'; for (int i = 1 ; i <= groupCnt ; ++i) { std::cout << indices[i].size(); for (int &idx : indices[i]) { std::cout << ' ' << idx; } std::cout << '\n'; } } void input() { readInt(n); readLong(b); for (int i = 1 ; i <= n ; ++i) { readInt(s[i].h); } for (int i = 1 ; i <= n ; ++i) { readLong(s[i].p); } for (int i = 1 ; i <= n ; ++i) { s[i].idx = i; // inputS[i] = s[i]; } } void fastIOI() { freopen("sticks.in", "r", stdin); freopen("sticks.out", "w", stdout); std::ios_base :: sync_with_stdio(0); std::cout.tie(nullptr); std::cin.tie(nullptr); } void fixEnds() { for (int i = 1 ; i <= solGroups ; ++i) { llong sum = 0; for (const int &idx : sol[i]) { sum += inputS[idx].h; } for (int j = 0 ; j < sol[i].size() ; ++j) { if (sum - inputS[sol[i][j]].h < b && inputS[sol[i][j]].p < inputS[sol[i].back()].p) { std::swap(sol[i][j], sol[i].back()); } } } } void fixEnds2() { for (int i = 1 ; i <= solGroups ; ++i) { llong sum = 0; for (const int &idx : sol[i]) { sum += s[idx].h; } for (int j = 0 ; j < sol[i].size() ; ++j) { if (sum - s[sol[i][j]].h < b && s[sol[i][j]].p < s[sol[i].back()].p) { std::swap(sol[i][j], sol[i].back()); } } } } short isEnd[MAXN]; llong allowed[MAXN]; void ennactSwap(int x, int y) { // std::cout << "ennact swap: " << x << ' ' << y << '\n'; int xVector = -1; int yVector = -1; int xPos = -1; int yPos = -1; for (int i = 1 ; i <= solGroups && (xVector == -1 || yVector == -1) ; ++i) { for (int j = 0 ; j < sol[i].size() && (xVector == -1 || yVector == -1) ; ++j) { if (sol[i][j] == x) { xVector = i; xPos = j; } if (sol[i][j] == y) { yVector = i; yPos = j; } } } assert(xVector != -1); assert(yVector != -1); std::swap(sol[xVector][xPos], sol[yVector][yPos]); } struct SetElement { llong allowed; llong penalty; int idx; friend bool operator < (const SetElement &a, const SetElement &b) { return a.allowed < b.allowed || (a.allowed == b.allowed && a.penalty < b.penalty) || (a.allowed == b.allowed && a.penalty == b.penalty && a.idx < b.idx); } }; bool tryFind() { std::fill(isEnd + 1, isEnd + 1 + n, 0); for (int i = 1 ; i <= solGroups ; ++i) { llong sum = 0; for (int j = 0 ; j < (int)sol[i].size() - 1 ; ++j) { sum += inputS[sol[i][j]].h; } assert(sum < b); for (int j = 0 ; j < (int)sol[i].size() - 1 ; ++j) { allowed[sol[i][j]] = (sum + inputS[sol[i].back()].h <= b ? 0 : b - sum - 1 + inputS[sol[i][j]].h); } isEnd[sol[i].back()] = (sum + inputS[sol[i].back()].h > b ? 1 : 2); } std::vector in, end; for (int i = 1 ; i <= n ; ++i) { if (isEnd[s[i].idx] == 1) end.push_back(s[i].idx); else if (isEnd[s[i].idx] == 0) in.push_back(s[i].idx); } int ptr = 0; int maxIdx = -1; std::set set; int bestPenaltyDrop = 0; int idxOne = -1; int idxTwo = -1; for (int i = 0 ; i < end.size() ; ++i) { while (ptr < in.size() && inputS[in[ptr]].p < inputS[end[i]].p) { if (maxIdx == -1 || allowed[in[ptr]] > allowed[in[maxIdx]]) { maxIdx = ptr; } auto it = set.lower_bound({allowed[in[ptr]], 0, 0}); if (it == set.end() || it->penalty > inputS[in[ptr]].p) { while (set.size()) { auto it = set.lower_bound({allowed[in[ptr]], 0, 0}); if (it == set.begin()) break; it = std::prev(it); if (it->penalty < inputS[in[ptr]].p) break; set.erase(it); } set.insert({allowed[in[ptr]], inputS[in[ptr]].p, in[ptr]}); } ptr++; } if (maxIdx != -1 && allowed[in[maxIdx]] >= inputS[end[i]].h) { auto it = set.lower_bound({inputS[end[i]].h, 0, 0}); assert(it != set.end()); if (bestPenaltyDrop < inputS[end[i]].p - it->penalty) { idxOne = end[i]; idxTwo = it->idx; bestPenaltyDrop = inputS[end[i]].p - it->penalty; } } } if (idxOne == -1) { return false; } ennactSwap(idxOne, idxTwo); return true; } llong eval() { llong res = 1LL * solGroups * solGroups * solGroups; for (int i = 1 ; i <= solGroups ; ++i) { llong sum = 0; for (int j = 0 ; j < (int)sol[i].size() - 1 ; ++j) { sum += inputS[sol[i][j]].h; } if (sum >= b) { std::cout << "INVALID!: " << i << '\n'; exit(1); } if (sum + inputS[sol[i].back()].h > b) { res += inputS[sol[i].back()].p; } } return res; } int main() { double begTime = clock(); fastIOI(); input(); if (n <= 10) { solveExp(); return 0; } if (n > 100000) { auto [res, groupCnt] = solve(); solGroups = groupCnt; for (int i = 1 ; i <= solGroups ; ++i) { sol[i] = indices[i]; } std::sort(s + 1, s + 1 + n, [&](auto x, auto y) { return x.idx < y.idx; }); fixEnds2(); printRealSol(); return 0; } auto [res, groupCnt] = solve(); auto [res2, groupCnt2] = solve2(); if (res <= res2) { solGroups = groupCnt; for (int i = 1 ; i <= solGroups ; ++i) { sol[i] = indices[i]; } } else { solGroups = groupCnt2; for (int i = 1 ; i <= solGroups ; ++i) { sol[i] = indices2[i]; } } for (int i = 1 ; i <= n ; ++i) { inputS[i] = s[i]; } std::sort(inputS + 1, inputS + 1 + n, [&](auto x, auto y) { return x.idx < y.idx; }); std::sort(s + 1, s + 1 + n, [&](auto x, auto y) { return x.p < y.p; }); // std::cout << "before: " << eval() << '\n'; while ((clock() - begTime) / CLOCKS_PER_SEC < 4.0) { // std::cout << "Try\n"; if (!tryFind()) { // std::cout << "not found\n"; break; } // std::cout << "found\n"; fixEnds(); } // std::cout << "after: " << eval() << '\n'; fixEnds(); printRealSol(); return 0; }