[AGC002 E]Candy Piles

题意

给一个\(n\)个元素的数组\(a_i\),每次可以有两种操作:

  • 将所有数中最大的那个数整个删除
  • 将所有数减\(1\),如果减到\(0\)就删除

问如果两个人轮番操作,到最后谁不能进行操作了就输,问谁能赢。

  • \(1\leq N\leq 10^5\)
  • \(1\leq a_i \leq 10^9\)

分析

我们考虑,将整个数组从大到小排序,然后考虑像这样可以画出一个图表一样的东西,那么就必然每次删最大相当于是将最左边的删掉。然后我们考虑全部数减\(1\)的操作,这相当于是将最下面一行删掉。这个时候这个游戏就变成了一个更加简洁的东西:给一条单调不增的折线,每次可以往上和往右走,走到边界的时候输,问怎样胜利。这个问题很显然可以设例如在先手的视角,对于某一个坐标\((x,y)\),设出一个判定函数\(f(x,y)\)表示从\((x,y)\)出发是必胜还是必败。很好写出转移,当\((x+1,y)\)\((x,y+1)\)两个中存在一个必败的,那当前这个就是必胜的(只要走到那个必败的格子,后手就必败),反之当前格子必胜。

这样暴力计算显然是\(\text O(\sum a_i)\)的,复杂度爆炸所以没啥用= =但是你要是打个表出来,你会发现一个奇妙的规律:从\((0,0)\)\((1,1)\)\(\cdots\)\((v,v)\)一直都是同样的一个胜利情况!我们考虑这个结论其实同样很容易看出,考虑对于某一方,如果任意一个\((x+1,y+1)\)是必败状态,那么\((x,y+1)\)\((x+1,y)\)都是必胜状态,进一步\((x,y)\)是必败状态。于是我们可以推出,在对这一方来说,这样一条斜向上的线上面的胜负状态都是一样的,那么对于另一方因为是刚好反过来所以也是一样的。

我们考虑,我们走某个点刚好顶住了,这时我们可能有两种方式:一种是往上走,一种是往右走。这两个都很容易计算,我们只要看一下往两个方向走能够顶到什么点,那个点的胜负状态是怎样的就可算出答案,整体复杂度也显然是\(\text O(N)\)的。

# include <bits/stdc++.h>
# define rep(i,a,b) for(int i=a;i<=b;++i)
using namespace std;
const int maxn = 100010;
int read(){
	char c=getchar(); int x=0;
	for(;c<'0'||c>'9';c=getchar());
	for(;c>='0'&&c<='9';c=getchar())x=x*10+c-'0';
	return x;
}
int a[maxn];
int n;
int main(){
	n=read();
	rep(i,0,n-1)a[i]=read();
	sort(a+0,a+n);
	reverse(a+0,a+n);
	int p=0,y=0;
	for(;p+1<n&&a[p+1]>p+1;++p);
	for(;y<n&&a[y]>p;++y);
	if ((a[p]-p)&1 && (y-p)&1) puts("Second");
	else puts("First");
	return 0;
}
posted @ 2018-05-04 16:53  WenDavid  阅读(117)  评论(0编辑  收藏  举报