[2021.4.9多校省选模拟35]老魔杖 (博弈论,找规律(?))
题面
三大死亡圣器之一的老魔杖,被誉为最强魔杖,被 Harry 掰断后扔在悬崖下。
某一天,小 A 有幸在悬崖下捡到了这根魔杖,然而由于损坏严重,所以只能施一些简单的小魔法。恰好魔法界新兴起了一种小游戏,这个游戏是这样的:
地上有一些砖块,每一块的长度都是 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4 中的一个整数。每次一个人可以进行两种操作中的一种:
- 使用“粉身碎骨”咒,粉碎恰好 n n n 个长度为 n n n 的砖块。如果长度为 n n n 的砖块不足 n n n 个,则不能进行此操作。
- 使用“变形”咒,把一个长度大于 1 1 1 的砖块分为两个长度大于 0 0 0 的砖块。你必须保证分成的两个砖块长度都是正整数。
当某个人无法使用魔法时即算输。
每次游戏的初始局面是随机生成的。然而小 A 还有一个迷之身份——欧捱儿,所以他可以作弊,当给出一个局面时,就知道先手能否必胜。
输入格式
输入的第一行包含一个整数 T T T, 表示数据组数。
接下来 T T T 行,每行四个非负整数 a , b , c , d a,b,c,d a,b,c,d,表示本组数据中长度为 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4 的砖块分别有多少个。
输出格式
输出 T T T 行,每行一个整数:1 表示先手必胜,0 表示先手必败。
样例输入
3
4 2 1 0
0 0 1 0
7 0 1 0
样例输出
1
1
0
数据范围与提示
对于 30% 数据满足: a + 2 b + 3 c + 4 d ≤ 10 a+2b+3c+4d\leq 10 a+2b+3c+4d≤10。
对于 70% 数据满足: m a x { a , b , c , d } ≤ 14 max\{a,b,c,d\}\leq 14 max{a,b,c,d}≤14。
对于 100% 数据满足: 1 ≤ T ≤ 10 , m a x { a , b , c , d } ≤ 1 0 10000 1\leq T\leq10,max\{a,b,c,d\}\leq10^{10000} 1≤T≤10,max{a,b,c,d}≤1010000。
题解
看到 100% 的数据就被吓到了,隐约觉得这是个结论题。
再看 70% 的数据,居然这么小!于是直接打了 DP 骗分跑路。
这里的 d p [ a ] [ b ] [ c ] [ d ] dp[a][b][c][d] dp[a][b][c][d] 表示四种砖块数量各是 a , b , c , d a,b,c,d a,b,c,d 时的状态,注意上界: a ≤ 140 , b ≤ 62. c ≤ 32 , d ≤ 14 a\leq 140,b\leq 62.c\leq 32,d\leq 14 a≤140,b≤62.c≤32,d≤14 ,以及计算顺序就行了。
但是这题不能就这么完了啊。我们得寻找结论。比起麻烦的猜想和推导,还是找规律靠谱些。
于是我把 m a x { a , b , c , d } ≤ 14 max\{a,b,c,d\}\leq 14 max{a,b,c,d}≤14 的 dp 值都打了出来,发现对于固定的 b , c , d b,c,d b,c,d, a a a 从小(从0)变到大会让 DP 值为 { 1 , 1 , 1 , 1... } \{1,1,1,1...\} {1,1,1,1...}、 { 1 , 0 , 1 , 0 , . . . } \{1,0,1,0,...\} {1,0,1,0,...} 和 { 0 , 1 , 0 , 1 , . . . } \{0,1,0,1,...\} {0,1,0,1,...} 三种情况 ,于是我又分别把满足三种变化方式的 b , c , d b,c,d b,c,d 分别打了出来:
- 第一种 { 1 , 1 , 1 , 1 , . . . } \{1,1,1,1,...\} {1,1,1,1,...}:满足 ( b + d ) % 3 = 2 (b+d)\%3=2 (b+d)%3=2 。
- 第二种 { 1 , 0 , 1 , 0 , . . . } \{1,0,1,0,...\} {1,0,1,0,...}:满足 ( b + d ) % 3 = ( c + 1 ) % 2 (b+d)\%3=(c+1)\%2 (b+d)%3=(c+1)%2
- 第三种 { 0 , 1 , 0 , 1 , . . . } \{0,1,0,1,...\} {0,1,0,1,...}:满足 ( b + d ) % 3 = c % 2 (b+d)\%3=c\%2 (b+d)%3=c%2
所以规律就找到了!当且仅当 ( b + d ) % 3 = 2 (b+d)\%3=2 (b+d)%3=2 或 ( b + d ) % 3 = ( c + a + 1 ) % 2 (b+d)\%3=(c+a+1)\%2 (b+d)%3=(c+a+1)%2 时先手必胜!
CODE
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 10005
#define LL long long
#define DB double
#define ENDL putchar('\n')
#define lowbit(x) (-(x) & (x))
#define mymax(a,b,c,d) max(max((a),(b)),max((c),(d)))
LL read() {
LL f = 1,x = 0;char s = getchar();
while(s < '0' || s > '9') {if(s == '-')f=-f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
return f * x;
}
const int MOD = 1000000007;
int n,m,i,j,s,o,k;
bool dp[145][65][35][16];// 140 , 63 , 33 , 14
char A[MAXN],B[MAXN],C[MAXN],D[MAXN];
int main() {
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
int T = read();
memset(dp,1,sizeof(dp));
dp[0][0][0][0] = 0;
for(int d = 0;d <= 14;d ++) {
for(int c = 0;c <= 33;c ++) {
for(int b = 0;b <= 63;b ++) {
for(int a = 0;a <= 140;a ++) {
if(a+b+c+d && a+2*b+3*c+4*d <= 140) {
dp[a][b][c][d] = 1;
if(a>0) dp[a][b][c][d] &= dp[a-1][b][c][d];
if(b>1) dp[a][b][c][d] &= dp[a][b-2][c][d];
if(c>2) dp[a][b][c][d] &= dp[a][b][c-3][d];
if(d>3) dp[a][b][c][d] &= dp[a][b][c][d-4];
if(d) {
dp[a][b][c][d] &= dp[a+1][b][c+1][d-1];
dp[a][b][c][d] &= dp[a][b+2][c][d-1];
}
if(c) {
dp[a][b][c][d] &= dp[a+1][b+1][c-1][d];
}
if(b) {
dp[a][b][c][d] &= dp[a+2][b-1][c][d];
}
dp[a][b][c][d] ^= 1;
// if(1||(a+b*2+c*3+d*4) & 1) printf("%d ",dp[a][b][c][d]);
}
}
// if(dp[0][b][c][d] == 0 && dp[1][b][c][d] == 1) printf("bcd: %d %d %d",b,c,d);
// ENDL;
}
// ENDL;
}
// ENDL;
}
while(T --) {
int la,lb,lc,ld;
scanf("%s", A + 1);
la = strlen(A + 1);
scanf("%s", B + 1);
lb = strlen(B + 1);
scanf("%s", C + 1);
lc = strlen(C + 1);
scanf("%s", D + 1);
ld = strlen(D + 1);
int a = 100,b = 100,c = 100,d = 100;
if(mymax(la,lb,lc,ld) <= 3) {
sscanf(A+1,"%d",&a);
sscanf(B+1,"%d",&b);
sscanf(C+1,"%d",&c);
sscanf(D+1,"%d",&d);
}
if(mymax(a,b,c,d) <= 14) {
printf("%d\n",dp[a][b][c][d]);
continue;
}
a = b = c = d = 0;
for(int i = 1;i <= la;i ++) a = (a*10+(A[i]-'0')) % 2;
for(int i = 1;i <= lc;i ++) c = (c*10+(C[i]-'0')) % 2;
for(int i = 1;i <= lb;i ++) b = (b*10+(B[i]-'0')) % 3;
for(int i = 1;i <= ld;i ++) d = (d*10+(D[i]-'0')) % 3;
if((b+d)%3 == 2 || (b+d)%3 == (c+a+1)%2) printf("1\n");
else printf("0\n");
}
return 0;
}
// bs: (b+d)%3 = 2 , ((b+d)%3 = (c+a+1)%2)