图解 Codeforces 1353D Constructing the Array

原创文章: https://www.huilon.net.cn/article/14

Codeforces 1353D

Description

题意

给定一个长度为 n n n , 元素全为 0 0 0 的数组, 然后给定变量 i = 1 i=1 i=1. 一直执行以下操作, 直到不可操作为止:

1.选取一段数字全为 0 0 0, 长度最长的一个子段, 长度相同时, 选在整个数组最左边的一个子段
2.对于这个子段, 把最中间的元素赋值为 i i i, 如果长度 m m m 为偶数, 按 m 2 \dfrac{m}{2} 2m 作为最中间位置. 位置从 1 1 1 开始. 最后 i i i 自增

求最后的结果数组. 有多组测试样例

范围:

1 ≤ n ≤ 2 ⋅ 1 0 5 1≤n≤2\cdot10^5 1n2105

n n n 是指所有测试样例的n加起来的数

Tutorial

题解

每次需要取长度最长, 最左边的子段, 可以考虑使用优先队列来做这道题.

普通的队列是一种先进先出的数据结构,元素在队列尾追加,从队列头删除。
.
在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先出列。优先队列具有最高优先级级先出 (first in, largest out)的行为特征。
.
取自网络, 有改动. 作者不详

我们把子段的长度和子段的左边联合作为优先队列的优先级, 长度最长, 子段最左边, 则优先级最高.

值得注意的是, 优先队列的实现方法有很多中, 在 c++ 中, 可以使用 vector, queue, set 这几个数据结构来实现, 也可以直接使用现成的 privority_queue 数据结构.
不过实践发现, privority_queue 的效率很慢, 这道题超时了. 实践发现用 set 来实现, 速度会很快. :/

用 set 来实现优先队列, 用这个重载构造:

template<_Key, _Compare = less<_Key>, _Alloc = allocator<_Key>> class set{...}

_Compare 需要传入一个结构体, 结构体内重载运算符 bool operator() , 即 () 括号运算符.
有两个参数 a,b, 返回 true 时把 a 放在前面. 需要注意传入的参数必须是 const 修饰的引用&类型

每次我们把中间元素赋值后, 顺便把左右两边的子段加入进优先队列.

示意图:

分析得: 时间复杂度为 O ( n ) O(n) O(n)

有以下代码:

#include <iostream>
#include <set>
#include <cstring>
using namespace std;

struct lenCmp{ // 重载括号运算符
    bool operator() (const pair<int, int>& a, const pair<int, int>& b) {
        int lenOfA = a.second - a.first; // 长度 = 右边 - 左边
        int lenOfB = b.second - b.first;
        if(lenOfB != lenOfA) return lenOfA > lenOfB; // 当长度不相等时就按长度排列
        else return a.first < b.first; // 长度相等时按谁在左边排列
    }
};

int main() {
    int t; cin >> t;
    for (int _ = 0; _ < t; ++_) {
        set<pair<int, int>, lenCmp> segmentPQ; // 子段优先队列, PQ 是指 priory queue. 这个 segmentPQ 装载了 pair 类型的元素
        // pair 是一个二元数据结构, 可以使用 segmentPQ.first 和 segmentPQ.second 来操作 pair 内的两个元素.
        // 这里我存储 segmentPQ.first 是子段的左边, segmentPQ.second 是子段的右边
        int n; cin >> n;
        int a[n+1]; memset(a, 0, sizeof(a)); // 把数组的所有元素赋值为 0
        segmentPQ.insert({1, n}); // 第一个子段就是本身数组
        int i = 1; // i 初始为 1
        while (!segmentPQ.empty()) { // 直到队列中没有子段可以继续执行
            auto target = *segmentPQ.begin(); segmentPQ.erase(segmentPQ.begin()); // 取出最前面的元素(即优先级最高的元素), 然后把他从队列删掉
            int l = target.first; // 子段的左边
            int r = target.second; // 子段的右边
            int mid = (l+r)/2; // 子段的中间
            a[mid] = i++; // 中间元素赋值为 i, 然后自增
            if(mid > l) segmentPQ.insert({l, mid - 1}); // 把中间元素的左边的子段放进优先队列
            if(mid < r) segmentPQ.insert({mid + 1, r}); // 把中间元素的右边的子段放进优先队列
        }
        for (int k = 1; k <= n; ++k) {
            if(k != 1) cout << " ";
            cout << a[k];
        }
        cout << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值