HNOI2007 分裂游戏(博弈论)
很巧妙的一道题
题面放这:https://www.luogu.com.cn/problem/P3185
首先我们对于每一个巧克力豆拆开来分别考虑,我们发现这是一个muilt-SG问题
显然有转移方程SG[i] = mex{SG[j] xor SG[k] (j > i,k > i};
然后我们把每个位置的SG异或起来就是整个局面的SG
我们可以发现,如果一个位置i的巧克力豆是偶数,那这个位置的贡献一定是0,因为自己异或自己偶数次为0,否则就是SG(i)
接着我们可以枚举i,j,k看取完这三个后是不是后手必败,因为n<=21,就做完了
/*分裂游戏*/ #include<cstdio> #include<iostream> #include<cstring> #include<algorithm> using namespace std; #define ll long long const int maxn = 31; int a[maxn]; int SG[maxn]; int used[1010]; int cnt = 0; int n; int read(){ char c = getchar(); int x = 0; while(c < '0' || c > '9') c = getchar(); while(c >= '0' && c <= '9') x = x * 10 + c - 48,c = getchar(); return x; } int main() { int t = read(); while(t--){ memset(SG,0,sizeof(used)); int n = read(); for(int i = n; i >= 1; i--){ memset(used,0,sizeof(used)); for(int j = i + 1; j <= n; ++j) for(int k = j; k <= n; ++k) used[SG[j] ^ SG[k]] = true; for(int j = 0; ; ++j) if(!used[j]){ SG[i] = j; break; } } int ans = 0; for(int i = 1; i <= n; ++i){ a[i] = read(); if(a[i] & 1) ans = ans xor SG[i]; } int cnt = 0; int ei = 0,ej = 0,ek = 0; for(int i = 1; i <= n; ++i){ for(int j = i + 1; j <= n; ++j){ for(int k = j; k <= n; ++k){ if((ans ^ SG[i] ^ SG[j] ^ SG[k]) == 0){ cnt++; if(ei == 0 && ej == 0 && ek == 0){ ei = i,ej = j,ek = k; } } } } } printf("%d %d %d\n",ei-1,ej-1,ek-1); printf("%d\n",cnt); } return 0; }