2025“钉耙编程”中国大学生算法设计春季联赛(6)1005.挺准题解
-
题目大意:博弈论,给定若干个数轴上属于正半轴的区间,每次可以往左移动区间,但是不能重叠或是区间左端点非正,最后无法移动的人输。
-
首先我们将每个区间中包含了多少个数(不包含左右端点)看作是多少个小球,那么考虑一次移动区间,我们发现移动一次区间对于这个区间本身是没有任何影响的,影响的是这个区间左右两个区间,即往左移动一个区间,相当于把左区间的小球拿出来放到右区间,这启发我们从区间编号奇偶角度分别来看,每次操作就相当于在奇数或是偶数编号区间上进行小球不可逆的移动操作
-
那么考虑区间编号为奇数的情况,偶数的情况一模一样。由于每次操作是将靠左区间的小球移到靠右区间,所以我们从右往左进行编号,令编号为奇数的区间从右往左编号依次为1,2...。我们发现,如果仅考虑编号为偶数的区间(注意此时的编号是对于奇数区间的重新编号),如果将该区间的小球移动到下一个奇数区间,我们后手便可以将同样数量的小球移动到下一个偶数区间,即抵消了先手的影响,于是只有偶数区间有小球的情况对于先手就变成了必败态。那么再考虑奇数区间的小球,将其移动到偶数区间等价于直接拿走,所以在奇数区间上就变成了nim问题,求异或即可。
-
区间编号为偶数的情况是一样的。这其实就是阶梯博弈。而两个阶梯博弈合在一起,其实也就是将两个博弈对应的异或再异或起来,如果非零那么先手一定赢。可以这么想,即使有一个为0,另一个非零,先把为0的那一个操作完之后,此时依旧是先手,再来操作非0的那一个,最后结局依旧是先手必胜。而对于后手,在先手先拿了为0的一边的小球之后,此时两边异或都非零,他是没有办法同时将两边都变成0的,对他来说依旧是必败态。
-
最后注意代码细节即可,要倒着考虑
#include<bits/stdc++.h>
using namespace std;
int t;
int n;
vector<int> a,b;
void solve() {
cin >> n;
a.assign(n + 10,0);
b.assign(n + 10,0);
for(int i = 1;i <= n;i++) cin >> b[i],a[n - i + 1] = b[i];
int res = 0;
for(int i = 2;i <= n;i++) {
int d = a[i] - a[i + 1] - 1;
if((i - 1) % 4 == 1 || (i - 1) % 4 == 2) res ^= d;
}
if(res) cout << "Taki\n";
else cout << "Maki\n";
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin >> t;
while(t--) solve();
return 0;
}

浙公网安备 33010602011771号