洛谷P3480 [POI2009] KAM-Pebbles 题解
题目链接:P3480 [POI2009] KAM-Pebbles
题意:
有 $N$ 堆石子,除了第一堆外,每堆石子个数都不少于前一堆的石子个数。
两人轮流操作每次操作可以从一堆石子中移走任意多石子,但是要保证操作后仍然满足初始时的条件。
谁没有石子可移时输掉游戏。问先手是否必胜。
输入格式:
多组输入,第一行一个整数 $u$ 代表数据组数($1\le u\le 10$)
接下来共 $2u$ 行,每两行代表一组数据:
第一行只有一个整数 $n~(1\le n\le 1000)$,表示石子堆数;
第二行有 $n$ 个整数用空格隔开,第 $i$ 个整数 $a_i$ 表示第 $i$ 堆的石子个数,保证 $a_1\le a_2\le a_3\le \cdots\le a_n$。
对于每组数据,保证石子总数不超过 $10000$。
输出格式:
输出 $u$ 行,如果第 $i$ 组数据先手必胜,输出
TAK
,否则输出NIE
。
设 $a_i$ 为第 $i$ 堆石子的个数,记 $b_i = a_i - a_{i-1}$ 。当我们在第 $i$ 堆拿了 $x$ 个石子时,
会使 $b_i \gets b_i - x$ ,并且 $b_{i + 1} \gets b_{i + 1} + x$ 。这相当于把第 $i$ 堆中的 $x$ 个石子转移到了 $i+1$ 堆。
那么这就是一个翻转的阶梯博弈(阶梯 Nim 游戏),判断和 $n$ 奇偶性相同的堆的异或和是否为 $0$ 即可。
这里简单介绍一下阶梯博弈吧。
有 $n$ 堆石子,每次可以从第 $i$ 堆的石子中拿走一部分放到第 $i-1$ 堆中,
或者把第 $1$ 堆中的石子拿走一部分。无法操作的人算输。求先手是否有必胜策略。
结论:阶梯博弈先手必胜当且仅当奇数堆的石子数异或和不为 $0$ 。
证明 考虑先手的策略。考虑按照最优策略,首先将奇数堆中的石子转移到偶数堆,
- 当对方将奇数堆的石子转移到偶数堆,相当于进行对于奇数堆的普通 Nim 游戏,继续按最优策略即可;
- 当对方将偶数堆的石子转移到奇数堆,则我们可以把这部分石子继续往下拿,没有改变奇数堆的局势。
于是我们简单证明了阶梯博弈和奇数堆的 Nim 游戏等价。$\square$
时间复杂度 $\mathcal{O}(Tn)$
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define rep(i, a, b) for(int i = (a), i##END = (b); i <= i##END; i++)
#define Rep(i, a, b) for(int i = (a), i##END = (b); i >= i##END; i--)
#define N ((int)(1e5 + 15))
int a[N], b[N];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
int qwq; cin >> qwq;
while(qwq--)
{
int n, res = 0; cin >> n;
rep(i, 1, n) cin >> a[i];
rep(i, 1, n) b[i] = a[i] - a[i - 1];
for(int i = n; i > 0; i -= 2) res ^= b[i];
cout << (res ? "TAK" : "NIE") << '\n';
}
return 0;
}
参考文献: