CF949B A Leapfrog in the Array 【构造】

13 篇文章 0 订阅
7 篇文章 0 订阅

题解概述

从查询位逆向跳跃,回溯至构造前的起始跳转位,即可推导出起始跳转位所对应的数字值,操作时间复杂度为 O ( q l o g ( n ) ) O(qlog(n)) O(qlog(n)).


读者不难达成以下共识

  1. 在构造过程中,序列的前半部分数字将会保持原位不动

  2. 当有 n n n个数字时,初始序列共有 n − 1 n-1 n1个空位,因此总共需要 n − 1 n-1 n1次移动操作才能完成最终序列构造,即填满空位;

  3. 在构造过程中, i i i次操作的移动距离也为 i i i

  4. 在构造过程中,一个独特数字的首次操作移动距离总是奇数

  5. 一个独特数字的下一次移动距离会翻倍

由上述共识可得,无需推演全部构造过程,即可用 O ( l o g n ) O(logn) O(logn)的复杂度推出某一个数字的最终位置。


利用上述共识可逆向迭代回溯出查询位所对应的起始数字值

首先易得,奇数查询位 的对应值为 ⌊ x 2 ⌋ + 1 \lfloor\frac{x}{2}\rfloor + 1 2x+1 (见共识0) .

下面为 偶数查询位 的推导步骤:

  1. 查询位 x x x的左侧有 ⌊ x 2 ⌋ − 1 \lfloor\frac{x}{2}\rfloor - 1 2x1个初始填充空位;

  2. 由于共有 n − 1 n-1 n1次移动操作,查询位 x x x末次移动为第 ( n − 1 ) − ( ⌊ x 2 ⌋ − 1 ) (n-1) - (\lfloor\frac{x}{2}\rfloor - 1) (n1)(⌊2x1)次移动移动距离自然也是 ( n − 1 ) − ( ⌊ x 2 ⌋ − 1 ) (n-1) - (\lfloor\frac{x}{2}\rfloor - 1) (n1)(⌊2x1) (见共识2,第 i i i次操作的移动距离也为 i i i

  3. 由共识3,4可得,每次移动距离的上一次移动距离会减半,直至移动距离为奇数(首次移动),设2中的末次移动距离为 l e n len len,我们可通过迭代 l e n / = 2 len /= 2 len/=2向右回推求解起始位,直至 l e n len len为奇数,设推出查询位 x x x的起始位置为 p o s pos pos

  4. 构造前起始 p o s pos pos位的对应值为 ⌊ p o s 2 ⌋ + 1 \lfloor\frac{pos}{2}\rfloor + 1 2pos+1.


逆向迭代递推代码如下(附正向构造过程代码):

/*
    Backtracking code for construction
*/

#include <iostream>
using namespace std;

int main()
{
    long long int n, q;
    cin >> n >> q;
    long long mid = (n+1) / 2;
    for (int i = 0; i < q; i ++){
        long long int x;
        cin >> x;
        
        if (x % 2 == 1){
            cout << x / 2 + 1 << endl;
            continue;
        }
        
        // (n - 1) == tot empty cells number, or tot moves
        // (x/2 - 1) == pre empty cells number
        long long int firstStepLength_reverse = (n-1) - (x/2 - 1);
        
        long long int pos = x;
        while (firstStepLength_reverse % 2 != 1){
            pos += firstStepLength_reverse;
            firstStepLength_reverse /= 2;
        }
        
        pos += firstStepLength_reverse;
        
        cout << pos / 2 + 1 << endl;
    }
    return 0;
}

/*
    Construction code
*/

/*
#include <iostream>
using namespace std;

int main()
{
    long long int n, q;
    cin >> n >> q;
    long long int mid = (n+1) / 2;
    for (int i = 0; i < q; i ++){
        long long int x;
        cin >> x;
        
        if (x <= mid){
            cout << x * 2 - 1 << endl;
            continue;
        }
        
        long long int firstStepLength = (n-x+1) * 2 - 1;
        
        long long int pos = x * 2 - 1;
        while (pos > n){
            pos -= firstStepLength;
            firstStepLength *= 2;
        }
        cout << pos << endl;
    }
    return 0;
}
*/
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Sycamore_Ma

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值