[POI2016]Korale 堆+st表

本文深入解析了一道算法竞赛题目,通过巧妙的策略优化了搜索过程,避免了超时问题。文章详细介绍了如何利用堆和预处理技巧来快速找到最优解,同时提供了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Desription
给你n个数。现在你可以选择若干个数(也可以一个都不选),选择的价值为所有数的价值和。现在给所有可能的选择,先按权值从小到大排序,对于权值相同的,根据所用数字集合的标号的字典序从小到大排序。请输出第k小的选择的价值,以及所用的选择集合。


Sample Input
4 10
3 7 4 3


Sample Output
10
1 3 4


这题卡长卡了我一上午。。。
首先我们如果按照DFS的思想,每一次对于一个数,我们要么选,要么不选,但这样是肯定会T的,我们需要一些想法。
如果我希望使序列长度加1,那我肯定是选择前面一个最小的数。
如果我们使序列长度增长,那我们肯定就使选择一个当前的次小值将他替换当前队尾的值,这里你可以用超级钢琴的思想来做,然后用个堆维护。
然后关于字典序之类的,你可以用他pre的rank来瞎搞搞。
(关于卡长,有一个小技巧:把数组维数换一换就能快。。。
我本来st表是这么开的:int f[1000010][21]
给成这样就快了:int f[21][1000010]
至于能快多少,这道题里面,差不多有6,7s这样子。。。


#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;
inline int read() {
    int s = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
    return s * f;
}

struct node {
	LL sum;
	int l, r, x, pre, rank;
} p[3000010];
int o, heap[3000010];
LL a[1000010];
int Log[1000010], f[21][1000010], bin[21];
inline int _min(int x, int y) {return a[x] <= a[y] ? x : y;}
inline bool pd(int x, int y) {
	if(p[x].sum == p[y].sum) {
		if(p[x].x == p[y].x) return p[p[x].pre].rank < p[p[y].pre].rank;
		return p[x].x < p[y].x;
	} return p[x].sum < p[y].sum;
}

inline void up(int x) {
	while(x) {
		if(x / 2 && pd(heap[x], heap[x / 2])) swap(heap[x], heap[x / 2]), x /= 2;
		else break;
	}
}

inline void down(int x) {
	int s = x * 2;
	while(s <= o) {
		if(s < o && pd(heap[s + 1], heap[s])) s++;
		if(pd(heap[s], heap[x])) swap(heap[x], heap[s]), x = s, s *= 2;
		else break;
	}
}

inline int query(int l, int r) {
	register int hh = Log[r - l + 1];
	return _min(f[hh][l], f[hh][r - bin[hh] + 1]);
}

inline void print(int x) {
	printf("%d ", p[x].x);
	if(p[x].pre) print(p[x].pre);
}

int main() {
	int n = read(), k = read(); k--;
	if(k == 0) {
		printf("0\n");
		return 0;
	}
	a[0] = 999999999;
	register int i, j;
	for(i = 2; i <= n; ++i) Log[i] = Log[i >> 1] + 1;
	for(i = 1; i <= n; ++i) a[i] = read(), f[0][i] = i;
	bin[0]=1;
	for(i = 1; i <= 20; ++i) bin[i] = bin[i - 1] << 1;
	for(i = 1; bin[i] <= n; ++i) for(j = 1; j + bin[i - 1] <= n; ++j) f[i][j] = _min(f[i - 1][j], f[i - 1][j + bin[i - 1]]);
	int tp = 1;
	p[1].l = 1, p[1].r = n, p[1].x = query(1, n), p[1].sum = a[p[1].x], p[1].pre = 0;
	heap[o = 1] = 1;
	LL last = -1, hh = 0;
	while(k--) {
		int g = heap[1];
		swap(heap[1], heap[o--]);
		down(1);
		node now = p[g];
		if(!k) {printf("%lld\n", now.sum); print(g); return 0;}
		if(now.sum > last) last = now.sum, hh = 1;
		else hh++;
		p[g].rank = hh;
		if(now.x > 1) {
			int j = ++tp;
			p[j].l = 1, p[j].r = now.x - 1, p[j].x = query(1, now.x - 1);
			p[j].sum = now.sum + a[p[j].x], p[j].pre = g;
			heap[++o] = j; up(o);
		}
		if(now.l < now.x) {
			int j = ++tp;
			p[j].l = now.l, p[j].r = now.x - 1, p[j].x = query(now.l, now.x - 1);
			p[j].sum = now.sum + a[p[j].x] - a[now.x], p[j].pre = now.pre;
			heap[++o] = j; up(o);
		}
		if(now.x < now.r) {
			int j = ++tp;
			p[j].l = now.x + 1, p[j].r = now.r, p[j].x = query(now.x + 1, now.r);
			p[j].sum = now.sum + a[p[j].x] - a[now.x], p[j].pre = now.pre;
			heap[++o] = j; up(o);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值