0%

[ARC151C] 01 Game

这场做完B就去写作业了...

题目当时瞅了一眼,感觉不算特别难。

题目链接

题意

现在有 个格子,编号为 。对于每一个 ,格子 与格子 是相邻的。

最初,有 个方格上写有数字,第 个方格上写的数字是 ,每一个格子上面的数字只能是 。剩下的 个方格上没有数字。

现在,高桥和青木将轮流执行一下操作,高桥先执行。

选择一个上面没有数字的方格,在上面写下 ,需要保证这个方格与它相邻的方格,上面的数字不同。

如果一个人不能操作,游戏结束,那个不能操作的人就输了。

如果两个人都按最优方案进行操作,谁会赢?

题解

首先我们知道,这题是个博弈论(废话

我们会发现,假如有两段空白的区域,中间用若干个数字隔开,那么这两段在操作的时候是不会互相影响的。

并且两个人可执行的操作是完全相同的。

所以我们知道,这道题是一个公平博弈,而且使用SG函数大概率可以做出来。

不过这道题的SG函数比较复杂,因为需要多种情况进行讨论。

我们设 为一段长度为 的两端数字相等的空白区域的SG函数值。

为一段长度为 的两端数字不等的空白区域的SG函数值。

为一段长度为 的一端为格子边界()的空白区域的SG函数值。

为一段长度为 的两端都为格子边界的空白区域的SG函数值。

看起来很不好求,所以我们进行打表。

打出来的结果放在这里:

打表程序

会发现SG函数的特征还是比较明显的。

做到这里其实就可以直接使用 SG 函数解决了,时间复杂度是 的。

不过这个结论终究是打表看出来的,下面来证明一下:

证明

我们现在需要证明的是对于任何正整数 ,都有

是用数学归纳法。

首先对于 ,结论显然成立。

现在假设,对于一个 ,所有 都满足结论,现在证明对于 ,也满足结论。


对于

显然,一共有两种情况:

  1. 分成两段长度 的两端相同的空白段,此时可达到的 值为
  2. 分成两段长度 的两端不同的空白段,此时可达到的 值为

所以


对于

显然,一共有一种情况。

分成一段长度 的两端相同的空白段和一段长度 的两端不同的空白段。

可以达到的 值为

所以


对于

显然,一共有两种情况。

  1. 分成一段长度 的两端相同的空白段和一段长度 的一端为边界的空白段,设含边界的一段长度为

    则此时可以到达的 值为 ,并且可以知道:

  2. 分成一段长度 的两端不同的空白段和一段长度 的一端为边界的空白段,设含边界的一段长度为

    则此时可以到达的 ,并且可以知道,可以到达的所有 恰好为 的所有整数。

所以


对于

显然,一共有一种情况。

分成两端长度 的一端为边界的空白段,假设其中一段的长度为 ,则另一段的长度为

  1. 为偶数时, 恰好有一个是偶数,一个是奇数。

    此时

  2. 为奇数时, 奇偶性相同。

    时,,到达的 值为

    因为他们两个奇偶性相同,所以到达的 值不可能为奇数。

    此时

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<map>
using namespace std;
using ll=long long;
using pli=pair<ll,int>;
using pi=pair<int,int>;
template<typename A>
using vc=vector<A>;
inline int read()
{
int s=0,w=1;char ch;
while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
inline ll lread()
{
ll s=0,w=1;char ch;
while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
ll ans;
int m;
ll n;
ll get(int lst,int now,ll len)
{
if(len==0) return 0;
if(lst==-1||now==-1)
{
if(lst==-1&&now==-1) return len&1;
if(len==1) return 1;
return len;
}
return lst==now;
}
int main()
{
n=lread(),m=read();
ll lst=0;int type=-1;
for(int i=1;i<=m;i++)
{
ll now=lread();int ty=read();
ans^=get(type,ty,now-lst-1);
lst=now,type=ty;
}
ans^=get(type,-1,n-lst);
if(ans) printf("Takahashi\n");
else printf("Aoki\n");
return 0;
}

感谢观看!