给出一个有 1 到 n 这 n 个数的序列,每次可以进行删除所有下标为奇数的项,或者所有下标为偶数的项,问进行 k 次操作以后还剩下的一个项为几?
【解法】
观察到 n 最大能达到 10^{18},思考出暴力不行并且要开 long long。那么我们就可以使用一个思想,就是不要总去思考要如何快速去标记这个序列,哪些数是被删去了的,而要去思考问题的本质,思考如何找出剩下的那个项。
设 x 为最终的答案。对于每次操作,考虑令 x 减去某个数使得所有操作结束后就是答案。x 的初始值为 n。当 a_i 为 1 时,我们看 n,如果为 2 的倍数(这里 n 实时记录了当前序列项的个数),那么直接整除 2,否则,也是整除 2,但另外需要令我们最后的答案 x 减去已经处理好的值 f。(具体如何处理 f 后续会讲到)
否则 a_i 为 2,与上面消除下标为奇数的项不同的是,因为这次消除的是下标为偶数的项,所以在 n 为 2 的倍数时,才令 x 减去 f,同样整除 2,但这里要注意了,为奇数时是整除 2 再加 1。
(附:这里的 $a_i$ 我在代码中使用 $y$ 代替了。)
----
【**原因**】
为什么是上述做法呢?
$f$ 初值为 $1$ 是因为在第 $1$ 次操作时每个项都对应了它的下标,因此令 $x$ 减去 $1$ 是为了使其不冲突。比如 $a_i$ 为 $1$,在 $n$ 为奇数是要修改 $x$ 的值,那么个数为奇数肯定是不可能会出现奇数的,因为这是在第一次操作,所有的奇数都会被删去,因此要将 $x$ 减 $1$,令其变为偶数,这样才不会产生矛盾。
后几轮的也是类似,为了不矛盾,我们会反复将 $x$ 变为奇数或偶数,同时也能保证此时的 $x$ 是存在的,没有被删除的,因为在第一轮结束后,能保证 $x$ 后几轮都为偶数,因为 $f$ 是不停乘 $2$。这样也可以保证剩下的项全是偶数,因为在第一轮就把所有的奇数全部删了。
当然,$a_i$ 为 $2$ 是也是同理,就不再做解释。
----
【**代码**】
```cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int T;
void solve() {
ll n, k, x;
scanf("%lld%lld", &n, &k);
x = n;
ll y, f = 1;
for (int i = 1; i <= k; i++) {
scanf("%lld", &y);
if (y == 1) {
if (!(n % 2))
n /= 2;
else {
x -= f;
n /= 2;
}
}
else {
if (!(n % 2)) {
x -= f;
n /= 2;
}
else
n = n / 2 + 1;
}
f *= 2;
}
printf("%lld\n", x);
}
int main(){
scanf("%d", &T);
for (int ti = 1; ti <= T; ti++) {
solve();
}
}
```