列表

详情


NC20055. [HNOI2007]分裂游戏

描述

聪聪和睿睿最近迷上了一款叫做分裂的游戏。 该游戏的规则试: 共有 n 个瓶子, 标号为 0,1,2.....n-1, 第 i 个瓶子中装有 p[i]颗巧克力豆,两个人轮流取豆子,每一轮每人选择 3 个瓶子。标号为 i,j,k, 并要保证 i < j , j < = k 且第 i 个瓶子中至少要有 1 颗巧克力豆,随后这个人从第 i 个瓶子中拿走一颗豆 子并在 j,k 中各放入一粒豆子(j 可能等于 k) 。如果轮到某人而他无法按规则取豆子,那么他将输掉比赛。胜利者可以拿走所有的巧克力豆! 
两人最后决定由聪聪先取豆子,为了能够得到最终的巧克力豆,聪聪自然希望赢得比赛。他思考了一下,发现在有的情况下,先拿的人一定有办法取胜,但是他不知道对于其他情况是否有必胜 策略,更不知道第一步该如何取。他决定偷偷请教聪明的你,希望你能告诉他,在给定每个瓶子 中的最初豆子数后是否能让自己得到所有巧克力豆,他还希望你告诉他第一步该如何取,并且为 了必胜,第一步有多少种取法? 假定 1 < n < = 21,p[i] < = 10000

输入描述

输入文件第一行是一个整数t表示测试数据的组数,接下来为t组测试数据(t ≤ 10)。
每组测试数据的第一行是瓶子的个数n,接下来的一行有n个由空格隔开的非负整数,表示每个瓶子中的豆子数。

输出描述

对于每组测试数据,输出包括两行,第一行为用一个空格两两隔开的三个整数,表示要想赢得游戏,第一步应该选取的3个瓶子的编号i,j,k,如果有多组符合要求的解,那么输出字典序最小的一组。如果无论如何都无法赢得游戏,那么输出用一个空格两两隔开的三个-1。第二行表示要想确保赢得比赛,第一步有多少种不同的取法。

示例1

输入:

2                          
4                            
1 0 1 5000            
3                            
0 0 1

输出:

0 2 3
1
-1 -1 -1
0

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++(g++ 7.5.0) 解法, 执行用时: 3ms, 内存消耗: 556K, 提交时间: 2023-08-03 05:38:49

#include <bits/stdc++.h>

using namespace std;
const int N = 30, M = 1e4 + 10;
int n, p[N], sg[N];

int g(int i) {
    if (~sg[i]) return sg[i];
    
    bool st[M];
    memset(st, 0, sizeof st);
    for (int j = i + 1; j < n; j++) {
        for (int k = j; k < n; k++) {
            st[g(j) ^ g(k)] = 1;
        }
    }
    for (int mex = 0;; mex++) if (!st[mex]) return sg[i] = mex;
}

void solve() {
    scanf("%d", &n);
    for (int i = 0; i < n; i++) scanf("%d", &p[i]);
    memset(sg, -1, sizeof sg), sg[n - 1] = 0;
    
    int nim = 0;
    for (int i = 0; i < n; i++) if (p[i] & 1) nim ^= g(i);
    if (!nim) {
        printf("-1 -1 -1\n0\n");
        return;
    }
    
    int cnt = 0;
    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
            for (int k = j; k < n; k++) {
                if ((nim ^ g(i) ^ g(j) ^ g(k)) == 0) {
                    if (!cnt++) printf("%d %d %d\n", i, j, k);
                }
            }
        }
    }
    printf("%d\n", cnt);
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) solve();
    return 0;
}

C++ 解法, 执行用时: 4ms, 内存消耗: 416K, 提交时间: 2022-05-13 22:13:25

#include<bits/stdc++.h>
using namespace std;
#define MAXN 105
int sg[MAXN], value[MAXN];
bool mark[MAXN];
int main() {
	sg[0] = 0;
	for (int i = 1; i < MAXN; i++) {
		memset(mark, false, sizeof(mark));
		for (int j = 0; j < i; j++)
		for (int k = j; k < i; k++)
			mark[sg[j] ^ sg[k]] = true;
		int ans = 0;
		while (mark[ans]) ans++;
		sg[i] = ans;
	}
	int T;
	scanf("%d", &T);
	while (T--) {
		int n, ans = 0;
		scanf("%d", &n);
		for (int i = 1; i <= n; i++) {
			scanf("%d", &value[i]);
			if (value[i] & 1) ans ^= sg[n - i];
		}
		if (ans == 0) {
			printf("-1 -1 -1\n0\n");
			continue;
		}
		int cnt = 0;
		for (int i = 1; i <= n; i++) {
			if (value[i] == 0) continue;
			for (int j = i + 1; j <= n; j++)
			for (int k = j; k <= n; k++)
				if ((ans ^ sg[n - i] ^ sg[n - j] ^ sg[n - k]) == 0) {
					if (cnt == 0) printf("%d %d %d\n", i - 1, j - 1, k - 1);
					cnt++;
				}
		}
		printf("%d\n", cnt);
	}
	return 0;
}

上一题