【题解】洛谷 P10765 「CROI · R2」在相思树下 I
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;
}