[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;
}
posted @ 2019-11-04 23:08  JZYshuraK_彧  阅读(176)  评论(0编辑  收藏  举报