题解 CF436E 【Cardboard Box】

龙之吻—水货

2019-03-12 08:13:57

Solution

# Cardboard Box ## 题目大意 你正在玩一个名为割绳子的游戏,当前单元有 $n$ 关游戏,解锁下一单元需要 $w$ 颗星。对于每一关,得到 $1$ 颗星需要花费 $a_i$ 的时间,得到 $2$ 颗星需要花费 $b_i$ $(b_i > a_i)$,当然不得星不需要花费时间。问解锁下一单元至少需要多少时间,并输出方案。 ## 解题报告 这题,可以一眼秒出一个背包做法或者一个比较优秀的DP做法,但由于 $1 \leq a_i \leq 10^9$,而 $1 \leq n \leq 3e5$,显然使用动归并不健康,于是考虑贪心做法。 这题,似乎看起来也可以直接贪心(也就是按照$a_i,b_i$的某种关系进行贪心选取),但不幸的是,在CF比赛的时候某**t**姓红黑名网友尝试了直接贪心的做法,并且被 $hack$ 掉了,所以我们可能需要更好更稳定的贪心方法。 我们把每一关按照$b_i$从小到大排序,然后我们从$1$扫到$n$。 假设当前我们扫到$pos$,则表示的是 $1$ 到 $pos$ 的关卡中至少要得到一颗星, $pos + 1$ 到 $n$ 的关卡中至多得到一颗星。那么,显然 $1$ 到 $pos$ 的关卡中我们选取了 $pos$ 个 $a_i$,还需要从 $1$ 到 $pos$ 的$b_i - a_i$ 和 $pos + 1$ 到 $n$ 的 $a_i$ 这 $n$ 个数中选取前 $w - pos$ 小的,每次向前推进的时候需要删除一个 $a_i$,加入一个 $b_i - a_i$。显然类似线段树,树状数组,平衡树等数据结构可以很轻松地完成这个任务。(当然,如果出现了不合法的情况跳过即可) 考虑这样贪心的正确性,对于每一个 $pos$,如果 $pos$ 后面有选 $2$ 颗星的情况,显然不会比把其中的 $1$ 颗星转移到前面的关卡完成更优。而从 $1$ 扫到 $n$ ,则将所有可能的情况包含在内。~~感性理解一下这是正确的~~ 至于如何输出方案,可以尝试在树上走路的方法,这里就不多说了 QwQ 附上我也不知道为什么写了这么长的代码 QwQ ```cpp #include<cstdio> #include<cstring> #include<algorithm> #include<tr1/unordered_map> #include<vector> class SegmentTree{ private : static const int maxn = 6e5 + 7; static const int INF = 1e9 + 7; typedef long long ll; struct Node{ int val, size; ll sum; Node *child[2]; Node() : val(0), size(0), sum(0) { child[0] = child[1] = NULL; } }; int n; Node *root, *tp, pool[maxn << 2]; Node *newNode() { *++tp = Node(); return tp; } void update(Node *now) { now->size = 0; now->sum = 0; for (register int i = 0; i < 2; i++) { if (now->child[i]) { now->size += now->child[i]->size; now->sum += now->child[i]->sum; } } } void addVal(Node *now, int left, int right, int pos, int val) { if (left == right) { now->val = val; now->sum += val; now->size++; return; } int mid = (left + right) >> 1; if (pos <= mid) { if (!now->child[0]) { now->child[0] = newNode(); } addVal(now->child[0], left, mid, pos, val); } else { if (!now->child[1]) { now->child[1] = newNode(); } addVal(now->child[1], mid + 1, right, pos, val); } update(now); } void delVal(Node *now, int left, int right, int pos, int val) { if (left == right) { now->sum -= val; now->size--; return; } int mid = (left + right) >> 1; if (pos <= mid) { if (!now->child[0]) { now->child[0] = newNode(); } delVal(now->child[0], left, mid, pos, val); } else { if (!now->child[1]) { now->child[1] = newNode(); } delVal(now->child[1], mid + 1, right, pos, val); } update(now); } ll query(Node *now, int left, int right, int tot) { //printf("tot : %d\n", tot); if (left == right) { return 1ll * now->val * tot; } int leftsize = now->child[0] ? now->child[0]->size : 0; int mid = (left + right) >> 1; ll res = 0; if (tot >= leftsize) { if (now->child[0]) { res += now->child[0]->sum; } if (tot > leftsize) { res += query(now->child[1], mid + 1, right, tot - leftsize); } return res; } else { return query(now->child[0], left, mid, tot); } } void search(Node *now, int left, int right, int tot, std::vector<int> &vec) { if (!now) { return; } if (left == right) { while (tot--) { vec.push_back(left); } } int leftsize = now->child[0] ? now->child[0]->size : 0; int mid = (left + right) >> 1; if (tot >= leftsize) { search(now->child[0], left, mid, leftsize, vec); if (tot > leftsize) { search(now->child[1], mid + 1, right, tot - leftsize, vec); } } else { search(now->child[0], left, mid, tot, vec); } } public : void init(int x) { n = x; tp = pool; root = newNode(); } void addVal(int pos, int val) { addVal(root, 1, n, pos, val); } void delVal(int pos, int val) { delVal(root, 1, n, pos, val); } ll query(int tot) { //printf("%d\n", tot); //printf("%d : %d\n", root->size, tot); return query(root, 1, n, tot); } void search(int tot, std::vector<int> &vec) { search(root, 1, n, tot, vec); //printf("%d %d\n", tot, vec.size()); } }; class Solution{ private : typedef std::pair<std::pair<int, int>, int> par; typedef long long ll; typedef std::vector<int>::iterator it; static const int maxn = 3e5 + 7; int n, m, p, res[maxn], num[maxn * 3], cnt, pos; par val[maxn]; ll ans, sum, pre; std::vector<int> vec, ps[maxn * 3]; //std::tr1::unordered_map<int, std::vector<int> > ps; std::tr1::unordered_map<int, int> id; SegmentTree tree; public : Solution() { get(); solve(); } void get() { scanf("%d %d", &n, &m); pos = std::max(m - n + 1, 1); for (register int i = 1, x, y; i <= n; i++) { scanf("%d %d", &x, &y); //num[i] = x, num[i + n] = y; num[i] = x, num[i + n] = y - x; val[i] = std::make_pair(std::make_pair(y, x), i); } } void solve() { std::sort(val + 1, val + 1 + n); std::sort(num + 1, num + 1 + (n << 1)); for (register int i = 1; i <= (n << 1); i++) { if (num[i] != num[i - 1]) { id[num[i]] = ++cnt; } } tree.init(cnt); for (register int i = 1; i < pos; i++) { ans += val[i].first.second; res[val[i].second]++; tree.addVal(id[val[i].first.first - val[i].first.second], val[i].first.first - val[i].first.second); } for (register int i = pos; i <= n; i++) { tree.addVal(id[val[i].first.second], val[i].first.second); } sum = tree.query(std::min(n, m)); p = pos - 1; for (register int i = pos; i <= n; i++) { pre += val[i].first.second; tree.delVal(id[val[i].first.second], val[i].first.second); tree.addVal(id[val[i].first.first - val[i].first.second], val[i].first.first - val[i].first.second); if (std::min(n, m) <= (i - pos + 1)) { continue; } ll q = tree.query(std::min(n, m) - (i - pos + 1)); if (pre + q < sum) { sum = pre + q; p = i; } } tree.init(cnt); for (register int i = 1; i < pos; i++) { tree.addVal(id[val[i].first.first - val[i].first.second], val[i].first.first - val[i].first.second); } for (register int i = pos; i <= n; i++) { tree.addVal(id[val[i].first.second], val[i].first.second); } printf("%lld\n", ans + sum); for (register int i = pos; i <= p; i++) { res[val[i].second]++; tree.delVal(id[val[i].first.second], val[i].first.second); tree.addVal(id[val[i].first.first - val[i].first.second], val[i].first.first - val[i].first.second); if (i == p) { tree.search(std::min(n, m) - (i - pos + 1), vec); } } if (p == pos - 1) { tree.search(std::min(n, m), vec); } for (register int i = 1; i <= n; i++) { if (i <= p) { num[i] = val[i].first.first - val[i].first.second; } else { num[i] = val[i].second; } } for (register int i = 1; i <= n; i++) { if (i <= p) { ps[id[val[i].first.first - val[i].first.second]].push_back(val[i].second); } else { ps[id[val[i].first.second]].push_back(val[i].second); } } for (register it i = vec.begin(); i != vec.end(); ++i) { int now = *i; //printf("%d\n", *i); //printf("%d\n", *i); //printf("%d\n", now); res[ps[now].back()]++; ps[now].pop_back(); } for (register int i = 1; i <= n; i++) { printf("%d", res[i]); } putchar('\n'); } }; Solution sol; int main() {} /*QwQ*/ ```