atcoder agc 002 E的解题有感

题目:atcoder agc 002 E.

题目大意:给出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的题的代码都好短啊.

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/80641815