题意:
桌上有N 堆糖果,第i 堆糖果有Ai 个糖。两人在玩游戏,轮流进行,每次进行下列两个操作中的一个
1.将当前最大的那堆糖果全部吃完
2.将每堆糖果吃掉一个
吃完的人输,假设两人足够聪明,问谁能必胜 1<=n<=1e5
输出First(表示第一个人必胜),或Second(表示第二个人必胜)
题解:
这题感觉没有图不好讲啊,真是道很神的题啊。先放个官方题解的链接。下面的图片来自官方题解。
官方题解
我们可以把a数组先从大到小排序,排序后可以看作下图,其中高度表示
a
i
a_i
ai的数值。
我们之后可以把删除一堆和每一堆去掉一个看作下图:
然后我们可以把一开始的图看作一个方格图,把删除一堆看作向右走,把每一堆减少一个看作向上走一步,轮廓线对应原图中的结束状态。如下图所示:
对于这种博弈思想的题,经常是判断每个状态是先手必胜还是先手必败,这里也是用这种思路,我们用’x’表示先手必败,用’o’表示先手必胜。首先显然对于终结节点是先手必胜,因为已经所有的糖都被吃掉了。然后我们可以暴力计算其他所有点的状态,但是这题显然会T。我们考虑观察这个状态图(如下),我们会发现,在同一条对角线上的点的状态都是一样的,要么都是必胜,要么都是必败。那么我们只需要一直沿着对角线走,直到走到边缘。我们发现走到边缘之后,如果走到最上方或者最右侧有至少一种能用奇数步完成,那么就是必胜的,否则就是必败的。于是就可以快速计算这题的答案了。
代码:
#include <bits/stdc++.h>
using namespace std;
int n,a[100010];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
sort(a+1,a+n+1);
reverse(a+1,a+n+1);
for(int i=1;i<=n;++i)
{
if(i+1>a[i+1])
{
int ji=i+1,pd=0;
while(a[ji]==i)
{
pd^=1;
++ji;
}
if(pd||(a[i]-i)%2==1)
printf("First\n");
else
printf("Second\n");
break;
}
}
return 0;
}