题目描述
题解
先考虑只有两堆石子的特性。
根据一个叫做威佐夫博弈的东西,我们发现必败态可以表示为: ( 1 , 2 ) , ( 3 , 5 ) , ( 4 , 7 ) , ( 6 , 10 ) . . . . . . (1,2),(3,5),(4,7),(6,10)...... (1,2),(3,5),(4,7),(6,10)......
由此,我们可以从中找到两个规律:
- 每一个自然数只能出现一次。
- 每一对必败态的两数之差都不一样。
我们可以分析一下为什么会出现这样的情况:
- 假设有 ( x , y ) (x,y) (x,y)和 ( x , y ′ ) (x,y') (x,y′)都是必败态,那么一定可以从y或y’取一部分石子来得到对方,两者可以互相转移。
- 假设有 ( x , y ) (x,y) (x,y)和 ( x + k , y + k ) (x+k,y+k) (x+k,y+k),则后者可以取出k个石子转移到了前者。
- 也就是说如果存在这两种情况,两个必败态是可以相互转移的。然而必败态是不能相互转移的。所以这样的情况是不可能存在的。同时也验证了上述两种规律的正确性。
思考一下如何拓展到3堆石子,显然根据必败态不能相互转移这一个结论:
- 存在必败态 ( x , y , z ) (x,y,z) (x,y,z),必然不存在必败态 ( x , y , z ′ ) (x,y,z') (x,y,z′)其中 z = ̸ z ′ z=\not z' z≠z′.我们可以用 f [ x ] [ y ] f[x][y] f[x][y]来表示这个 z z z。
- 我们可以考虑如何求解 f [ x ] [ y ] . f[x][y]. f[x][y].
我们可以枚举这个z,求解有多少个 f [ x ] [ y ] = z f[x][y]=z f[x][y]=z.我们可以类比威佐夫博弈来发现一些性质:
- 如果 f [ x ] [ y ] = k < z f[x][y]=k<z f[x][y]=k<z,则 ( x + z − k , y + z − k , z ) (x+z-k,y+z-k,z) (x+z−k,y+z−k,z)一定不可能是必败态,因此可以同时取走 z − k z-k z−k个石子来保证相互转移。同时, ( x + z − k , y ) (x+z-k,y) (x+z−k,y)和 ( x , y + z − k ) (x,y+z-k) (x,y+z−k)也意味着不存在必败态。
- 如果显然,对于每一个 z z z来说,不可能出现两组必败态 ( x 1 , y 1 , z ) (x_1,y_1,z) (x1,y1,z)和 ( x 2 , y 2 , z ) (x_2,y_2,z) (x2,y2,z)满足 ∣ x 1 − y 1 ∣ = ∣ x 2 − y 2 ∣ |x_1-y_1|=|x_2-y_2| ∣x1−y1∣=∣x2−y2∣,来避免同时取两堆石子进行转移。
- 显然不能存在两组必败态 ( x 1 , y 1 , z ) (x_1,y_1,z) (x1,y1,z)和 ( x 2 , y 2 , z ) (x_2,y_2,z) (x2,y2,z)满足 x 1 = x 2 x_1=x_2 x1=x2或 y 1 = y 2 y_1=y_2 y1=y2.
然后就可以用一堆数组标记就可以啦~
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N = 310;
int f[N][N], cnt[N], dlt[N], vis[N*2][N*2];
void work(void)
{
memset(f,30,sizeof f);
f[0][0] = 0;
for (int i=0;i<=300;++i)
{
memset(dlt,0,sizeof dlt);//对两种石子同时取得情况进行判重(两数之差)
memset(cnt,0,sizeof cnt);//对每次取一堆石子的情况进行判重
memset(vis,0,sizeof vis);//对三堆石子同时取得情况进行判重
for (int j=0;j<=300;++j)
for (int k=0;k<=j;++k)
{
if (f[j][k] < i) {
int val = i - f[j][k];
vis[j][k+val] = vis[j+val][k] = vis[j+val][k+val] =
vis[k][j+val] = vis[k+val][j] = 1;
}
else if (!cnt[j] && !cnt[k] && !vis[j][k] && !vis[k][j] && !dlt[abs(j-k)])
f[j][k] = f[k][j] = i, cnt[j] = cnt[k] = vis[j][k] = vis[k][j] = dlt[abs(j-k)] = 1;
}
}
return;
}
int main(void)
{
freopen("input.in","r",stdin);
freopen("output.out","w",stdout);
work();
int T; cin>>T;
while (T --)
{
int a,b,c; scanf("%d %d %d", &a, &b, &c);
f[a][b] == c ? puts("No") : puts("Yes");
}
return 0;
}