1 条题解
-
0
思路与算法
二分每个人最多抄写的页数B,范围为最大单本页数到全部页数和。顺序贪心把当前人尽量装满,所需人数≤k即B可行。求出最小B后,从最后一本倒着划分:给最后的人尽量多装,同时为前面每个人各留至少一本,再依次向前。输出每段书号。
正确性说明
固定B时,任何方案第一段的结束位置都不可能超过贪心结束位置;依次交换可知贪心所需人数最少。若少于k段,拆分非空段即可达到k段,故判定等价且单调。逆序重构在后面各段容量B内尽量吸收书,使前面剩余页数最少;若一个更早的划分能进一步减少最前未固定者的页数,则余下书本必须能由相同数量的后段容纳,与逆序最远吸收边界矛盾。逐段得到题目要求的前者尽量少抄的规范答案。
复杂度
设P为页数总和、D为P位数。含读入和重构,时间O(mD log P),空间O(mD+k);二分始终使用整数。
易错点
每本书不可拆,k个人各抄连续非空段且顺序不变;多解必须遵守前者少抄。页数无显式位数限制,标程使用大整数。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; struct cpp_int { string s; bool neg; cpp_int(long long v = 0) : s(to_string(v)), neg(v < 0) { if (neg) s.erase(s.begin()); } void norm() { auto p = s.find_first_not_of('0'); s = p == string::npos ? "0" : s.substr(p); if (s == "0") neg = false; } static int cmpabs(const cpp_int &a, const cpp_int &b) { if (a.s.size() != b.s.size()) return a.s.size() < b.s.size() ? -1 : 1; return a.s == b.s ? 0 : a.s < b.s ? -1 : 1; } friend bool operator<(const cpp_int &a, const cpp_int &b) { if (a.neg != b.neg) return a.neg; int c = cmpabs(a, b); return a.neg ? c > 0 : c < 0; } friend bool operator==(const cpp_int &a, const cpp_int &b) { return a.neg == b.neg && a.s == b.s; } friend bool operator!=(const cpp_int &a, const cpp_int &b) { return !(a == b); } friend bool operator>(const cpp_int &a, const cpp_int &b) { return b < a; } friend bool operator<=(const cpp_int &a, const cpp_int &b) { return !(b < a); } friend bool operator>=(const cpp_int &a, const cpp_int &b) { return !(a < b); } cpp_int operator-() const { cpp_int r = *this; if (r.s != "0") r.neg = !r.neg; return r; } friend cpp_int operator+(const cpp_int &a, const cpp_int &b) { cpp_int r; r.s.clear(); int carry = 0; if (a.neg == b.neg) { int i = (int)a.s.size() - 1, j = (int)b.s.size() - 1; while (i >= 0 || j >= 0 || carry) { int v = carry + (i >= 0 ? a.s[i--] - '0' : 0) + (j >= 0 ? b.s[j--] - '0' : 0); r.s += char('0' + v % 10); carry = v / 10; } r.neg = a.neg; } else { const cpp_int *x = &a, *y = &b; if (cmpabs(a, b) < 0) swap(x, y); int i = (int)x->s.size() - 1, j = (int)y->s.size() - 1; while (i >= 0) { int v = x->s[i--] - '0' - carry - (j >= 0 ? y->s[j--] - '0' : 0); carry = v < 0; if (carry) v += 10; r.s += char('0' + v); } r.neg = x->neg; } reverse(r.s.begin(), r.s.end()); r.norm(); return r; } friend cpp_int operator-(const cpp_int &a, const cpp_int &b) { return a + (-b); } friend cpp_int operator*(const cpp_int &a, int b) { cpp_int r; r.s.clear(); long long carry = 0; long long z = b; if (z < 0) z = -z; for (int i = (int)a.s.size() - 1; i >= 0; i--) { carry += (a.s[i] - '0') * z; r.s += char('0' + carry % 10); carry /= 10; } while (carry) { r.s += char('0' + carry % 10); carry /= 10; } reverse(r.s.begin(), r.s.end()); r.neg = a.neg ^ (b < 0); r.norm(); return r; } friend cpp_int operator/(const cpp_int &a, int b) { cpp_int r; r.s.clear(); long long rem = 0, z = b; if (z < 0) z = -z; for (char c : a.s) { rem = rem * 10 + c - '0'; r.s += char('0' + rem / z); rem %= z; } r.neg = a.neg ^ (b < 0); r.norm(); return r; } cpp_int &operator+=(const cpp_int &b) { *this = *this + b; return *this; } cpp_int &operator-=(const cpp_int &b) { *this = *this - b; return *this; } cpp_int &operator*=(int b) { *this = *this * b; return *this; } friend istream &operator>>(istream &in, cpp_int &a) { string v; if (in >> v) { a.neg = v[0] == '-'; if (v[0] == '-' || v[0] == '+') v.erase(v.begin()); a.s = v; a.norm(); } return in; } friend ostream &operator<<(ostream &out, const cpp_int &a) { if (a.neg) out << '-'; return out << a.s; } }; int main() { int m, k; cin >> m >> k; vector<cpp_int> a(m + 1); cpp_int lo = 0, hi = 0; for (int i = 1; i <= m; i++) { cin >> a[i]; lo = max(lo, a[i]); hi += a[i]; } auto possible = [&](const cpp_int &cap) { int used = 1; cpp_int sum = 0; for (int i = 1; i <= m; i++) { if (sum + a[i] > cap) { used++; sum = 0; } sum += a[i]; } return used <= k; }; while (lo < hi) { cpp_int mid = (lo + hi) / 2; if (possible(mid)) hi = mid; else lo = mid + 1; } vector<pair<int, int>> out(k); int j = m; for (int person = k; person >= 1; person--) { int end = j; cpp_int sum = 0; while (j >= person && sum + a[j] <= lo) { sum += a[j]; j--; } out[person - 1] = {j + 1, end}; } for (auto p : out) cout << p.first << " " << p.second << "\n"; }
- 1