[bzoj4345][POI2016]Korale_堆_贪心_线段树_dfs
Posted shurak
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[bzoj4345][POI2016]Korale_堆_贪心_线段树_dfs相关的知识,希望对你有一定的参考价值。
bzoj4345 POI2016 Korale
题目链接:https://lydsy.com/JudgeOnline/problem.php?id=4345
数据范围:略。
题解:
由于$k$的范围问题,我们很容易想到优先队列。
至于从每个状态怎么往下一个转移就是这个题的精髓。
我们先考虑第一问:
第一问没有字典序的限制,我们把所有的数按照从小到大排序。
堆里维护二元组$(Sum, id)$表示这种选取方式的和位$Sum$,最大下标为$id$。
它可以转移到$(Sum - a_{id} + a_{id+1}, id+1)$和$(Sum + a_{id + 1}, id + 1)$。
这一想是显然的,但是不咋好想...有点超级钢琴的味道。
下面我们考虑第二问:
第二问我们爆搜即可,想求出来当前下标(不排序)到最后一个数这个区间内,小于当前剩余和的最小下标的数是啥,然后暴力搜下去即可。
这个过程可以用线段树维护。
至于复杂度为什么是对的?因为我们每时每刻都保证了所有的枚举和都是小于第一问的值的,即使枚举到了第一问的值也在接受范围内。
言外之意我们枚举的每一个值,都是前$k-1$中的一个。
代码:
#include <bits/stdc++.h> #define ls p << 1 #define rs p << 1 | 1 #define N 1000010 using namespace std; typedef long long ll; char *p1, *p2, buf[100000]; #define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++ ) int rd() { int x = 0; char c = nc(); while (c < 48) { c = nc(); } while (c > 47) { x = (((x << 2) + x) << 1) + (c ^ 48), c = nc(); } return x; } priority_queue <pair<ll, int> > q; int a[N], b[N], Same, mn[N << 2]; ll ans[N]; inline void pushup(int p) { mn[p] = min(mn[ls], mn[rs]); } void build(int l, int r, int p) { if (l == r) { mn[p] = b[l]; return; } int mid = (l + r) >> 1; build(l, mid, ls), build(mid + 1, r, rs); pushup(p); } int query(int x, ll y, int l, int r, int p) { if (x <= l) { if (mn[p] > y) { return 0; } if (l == r) { return l; } } int mid = (l + r) >> 1; if (x <= mid) { int mdl = query(x, y, l, mid, ls); if (mdl) { return mdl; } } return query(x, y, mid + 1, r, rs); } int top, st[N], n, k; void dfs(int p, ll re) { if (!Same) { return; } if (!re) { Same -- ; if (!Same) { for (int i = 1; i <= top; i ++ ) { printf("%d ", st[i]); } puts(""); } return; } for (int i = p + 1; i <= n; i ++ ) { i = query(i, re, 1, n, 1); if (i) { st[ ++ top] = i; dfs(i, re - b[i]); top -- ; } else { break; } } } int main() { n = rd(), k = rd() - 1; for (int i = 1; i <= n; i ++ ) { a[i] = b[i] = rd(); } sort(a + 1, a + n + 1); q.push(make_pair(-a[1], 1)); for (int i = 1; i <= k; i ++ ) { ans[i] = -q.top().first; int x = q.top().second; q.pop(); if (x < n) { q.push(make_pair(-(ans[i] - a[x] + a[x + 1]), x + 1)); q.push(make_pair(-(ans[i] + a[x + 1]), x + 1)); } } // for (int i = 1; i <= k; i ++ ) { // printf("%lld ", ans[i]); // } // puts(""); cout << ans[k] << endl ; for (int i = k; i; i -- ) { if (ans[i] != ans[k]) { break; } Same ++ ; } // cout << Same << endl ; build(1, n, 1); dfs(0, ans[k]); return 0; }
以上是关于[bzoj4345][POI2016]Korale_堆_贪心_线段树_dfs的主要内容,如果未能解决你的问题,请参考以下文章
bzoj4591 / P4345 [SHOI2015]超能粒子炮·改