【题解】洛谷 P10765 「CROI · R2」在相思树下 I

I 原题

P10765 「CROI · R2」在相思树下 I

II 题意简述

\(T\) 组测试数据。

对于每一组测试数据,有初始数列,共 \(n\) 个元素,从 \(1\)\(n\)。给出 \(k\) 次操作。操作一:将数列中下标为奇数的数全部删除;操作二:将数列中下标为偶数的数全部删除。完成操作之后,将剩余的数从下标 \(1\) 开始依次重新编排下标。求问 \(k\) 次操作之后剩下来的最后一个数是什么。

数据满足 \(1 \leq T \leq 10, 1 \leq n \leq 10^{18}\),所有 \(k\) 满足可以在操作 \(k\) 次后使数列仅剩一个元素。

III 解析

思考最后两步可能的情况。容易发现,满足这种情况的话,第 \(k - 1\) 步完成后仅可能剩 \(2\)\(3\) 个元素,其余情况都需要再进行多于 \(1\) 步才能做到仅剩一个数。如果第 \(k - 1\) 步操作完成后剩余 \(3\) 个元素,因为第 \(k\) 步执行完将仅剩一个元素,这第 \(k\) 次操作必为操作 \(1\)。此时剩下的元素就是第 \(k - 1\) 次操作完成后的数列的第二个元素。如果第 \(k - 1\) 步完成后剩 \(2\) 个元素,那么最后剩下的有两种情况:是 \(1\) 操作,剩下第 \(k - 1\) 次操作完成后的数列的第 \(2\) 个元素;是 \(2\) 操作,剩下第 \(k - 1\) 次操作完成后的数列的第一个元素。

综合一下,最后一步的操作如果是操作 \(1\),答案应为第 \(k - 1\) 次操作完成后的数列的第二个元素,如果为操作 \(2\),应为第 \(k - 1\) 次操作完成后的数列的第一个元素。

那么问题来了,如何知道这个关键的“第 \(k - 1\) 次操作完成后的数列的第一或第二个元素”?

观察这个数列的变化规律,我们能够发现,如果执行操作 \(1\),则数列的第一项变成数列的第二项,公差变为原来的二倍;如果执行操作 \(2\),则第一项不变,第二项变为第三项,公差变为原来的二倍。可以设变量 \(a, b, d\) 维护数列的第一项、第二项、公差。维护三个数据的思路在代码注释中,不再赘述。在进行到最后一次操作时,根据操作类型判断答案即可。

注意:

  • 必须给 \(a, b, d\) 赋初始值 \(1, 2, 1\)
  • 需要用 long long 来存储 \(n, k, a, b, d\)

IV 代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll t; // 测试数据组数
inline void read(ll& x){ // 快读
    ll s = 0, w = 1; char ch = getchar();
    while(ch < '0' || ch > '9') { w = (ch == '-' ? -1 : w); ch = getchar(); }
    while(ch >= '0' && ch <= '9') { s = s * 10 + ch - '0'; ch = getchar(); }
    x = s * w;
}
inline void write(ll x){ // 快写
    if(x < 0) { x = -x; putchar('-'); }
    if(x > 9) write(x / 10);
    putchar(x % 10 + '0');
}
inline void write(ll x, char ch) { write(x); putchar(ch); }
int main(){
    read(t);
    while(t--){
        ll n, k, a = 1, b = 2, d = 1; // n, k 表示初始数列长度,操作次数,a, b, d 分别维护数列的第一项、第二项、公差
        read(n), read(k);
        for(int i = 1; i <= k; i++){
            ll x; read(x);
            if(i == k)
                write((x == 1 ? b : a), '\n'); // 依据最后一步的操作种类来判断答案是数列第一项还是第二项
            if(x == 1){ // 若操作 1
                ll tmp = b;
                a = tmp; b = tmp + 2 * d; // 新数列第一项 a 变为原数列第二项 b, 新数列第二项变为原数列第四项
                // 数列第四项等于数列第二项加二倍公差
                d *= 2; // 更新数列公差
                // 如果先更新公差,b 加一倍的 d 即可
            }else if(x == 2){ // 若操作 2
                b += d; // 新数列第一项不变,新数列第二项等于原数列第三项
                // 原数列第三项等于原数列第二项加一倍公差
                d *= 2; // 更新公差
            }
        }
    }
    return 0;
}

V 记录

R165814303

posted @ 2024-07-13 21:54  Prülystic  阅读(51)  评论(0编辑  收藏  举报