题目大意:给出n堆糖果,第i堆糖果有ai颗糖果,每次可以吃完最多糖果的一堆糖果或者每堆糖果吃一颗,问先手还是后手可以肯定吃到最后一颗糖果.
这道题看起来是经典博弈论,可是是博弈论就不是atcoder agc的E题了.
这道题可以这样来想,因为每堆吃一颗糖不会影响顺序,所以我们先把糖果按从大到小排序.
之后我们把这些糖果想象成一个二维平面,如下图:
我们可以知道,一开始的糖果在红色圆圈的位置.
那么我们吃掉一堆糖果相当于往右移了一个格子,每堆吃掉一颗糖果相当于向上移了一个格子.
当我们不能再移的时候,就是说明这个时候游戏结束了,当前一步的人无法继续操作,相当于输了.
那么我们就可以再次用记忆化搜索处理处一张表,表示每个点是否必胜.
就像这么一张图(0表示必败,1表示必胜):
这张图处理出来后,我们找找规律.
我们发现,每一条从左下往右上的斜线上的数字都是相等的.
这意味着我们可以快速找到边缘的那个点,然后我们就可以在判断一下这东西竖直向上有多少个格子,记为k1,水平向右有多少个格子,记为k2,然后只要k1和k2中有一个数是奇数,就是先手必胜.
那么这道题就可以O(n)扫一遍,二分可以做到O(log(n)),但是时间复杂度是O(nlog(n)).
其实瓶颈在于排序,没有排序在于输入输出.
于是我选择了O(n)写.
AC代码如下:
#include<bits/stdc++.h> using namespace std; const int N=100000; int a[N+5],n,x,y; inline void into(){ scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]); } bool cmp(int a,int b){ return a>b; } inline void work(){ sort(a+1,a+1+n,cmp); for (;x<=n&&x+1<=a[x+1];x++); for (y=x;y+1<=n&&a[y+1]==x;y++); } inline void outo(){ if (a[x]-x&1||y-x&1) printf("First\n"); else printf("Second\n"); } int main(){ into(); work(); outo(); return 0; }
atcoder的题的代码都好短啊.