蒟蒻君的刷题日记Day12(线段树专题T4):P8082 [COCI2011-2012#4] KEKS 线段树版题解

本文介绍了一种利用线段树解决构造最大数问题的方法。博主首先阐述了问题的基本思路,即从前向后依次确定每位数字,并通过线段树维护区间最大值。在每个步骤中,确定当前位数的区间,然后查询并更新线段树。最后,通过O(klogn)的时间复杂度得到了解决方案。代码实现部分展示了线段树的构建和查询过程。

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

解题思路

看题解区的大佬们用的都是单调栈,本蒟蒻献上一篇线段树题解。

整个数最大,首先位数是确定的,则肯定优先考虑高位大小。

大体思路就是从前向后依次求出每一位的值(好像是废话)。

  • 对于第 i i i 位,首先确定这个数在字符串中的位置哪个区间里。
    对于左边界,在上一个数后面(第一个数就是 1 1 1);对于右边界,右边肯定要有 n − k − i n-k-i nki 个数,最大是 k + i + 1 k+i+1 k+i+1

  • 然后我们就是求出这个区间中的最大值,当然下标也要确定。这一点就可以用线段树模板预处理,方法略。

  • 接着就是每次更新区间边界,然后 l o g n logn logn 查询,总时间复杂度为 O ( k l o g n ) O(klogn) O(klogn)

代码实现

#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
int n, k;
string a;
struct Sec {
	int maxv, idx;
} sec[N << 2];
inline Sec Max(Sec x, Sec y) {
	if (x.maxv != y.maxv) {
		return x.maxv > y.maxv ? x : y;
	}
	return x.idx < y.idx ? x : y;
}
inline void pushup(int id) {
	sec[id] = Max(sec[id << 1], sec[id << 1 | 1]);
}
void build(int id, int l, int r) {
    if (l == r) {
    	sec[id] = {a[l], l};
        return;
    }
    int mid = l + r >> 1;
    build(id << 1, l, mid);
    build(id << 1 | 1, mid + 1, r);
    pushup(id);
}
Sec query(int id, int l, int r, int x, int y) {
    if (x <= l && r <= y) {
		return sec[id];
    }
    int mid = l + r >> 1;
    Sec res = {-1, 0};
    if (x <= mid) {
        res = Max(res, query(id << 1, l, mid, x, y));
    }
    if (y > mid) {
        res = Max(res, query(id << 1 | 1, mid + 1, r, x, y));
    }
    return res;
}
string res;
int main() {
	ios :: sync_with_stdio(0);
	cin >> n >> k >> a;
	a = " " + a;
	for (int i = 1; i <= n; ++i) {
		a[i] ^= '0';
 	}
 	build(1, 1, n);
	int l = 1, r = k + 1;
	for (int i = 1; i <= n - k; ++i) {
		Sec s = query(1, 1, n, l, r);
		res += to_string(s.maxv);
		l = s.idx + 1;
		++r;
	}
	cout << res << '\n';
	return 0;
}

Good Good 贺题,Day Day Up!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蒟蒻一枚

谢谢鸭~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值