赞
踩
- #include <iostream>
-
- using namespace std;
-
- // 先手必胜状态:可以走到某一个必败状态
- // 先手必败状态:走不到任意一个必败状态
-
-
-
- /*
- 给定n堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。
- 问如果两人都采用最优策略,先手是否必胜。
- 输入格式
- 第一行包含整数n。
- 第二行包含n个数字,其中第i个数字表示第i堆石子的数量。
- 输出格式
- 如果先手方必胜,则输出"Yes"。
- 否则,输出"No"。
- 数据范围
- 1 ≤ n ≤ 10^5,
- 1 ≤ 每堆石子数 ≤ 10
- */
-
-
-
-
-
-
-
- // 定理:如果石子堆分别有a1,a2,a3,……,an, 若 a1^a2^a3^……^an = 0 的话,先手必败;不等于0,先手必胜
-
- /* 证明:先假定 a1 ^ a2 ^ a3 ^ …… ^ an = x (x != 0) 为先手必胜状态,
- a1 ^ a2 ^ a3 ^ …… ^ an = 0 为先手必败状态
- 证明:(1)通过改变a1~an中某个数值,一定可以将先手必胜状态转换为先手必败状态
- (2)改变a1-an中某个值,一定会将先手必败状态转换为先手必胜状态
- (1) x的二进制中最高位在第k位,则a1-an中必定存在一个数 ai 的第k位为1
- 则 ai^x < ai (把ai的第k位变为0了)
- 在 ai 堆中拿走 ai-(ai^x) 个石子,此时原ai堆石子数量为(ai^x)
- 拿完之后各石堆石子数量为:a1, a2, a3, ……, ai^x, ……, an
- 将它们异或起来:a1 ^ a2 ^ …… ^ (ai ^ x) ^ …… ^ an = x ^ x = 0
- 得证
- (2) 改变某个值ai,让其变为 aii
- 假设 a1 ^ a2 ^ …… ^ ai ^ …… ^ an = 0
- 又因为 a1 ^ a2 ^ …… ^ aii ^ …… ^ an = 0
- 上面2式子左边异或左边,右边异或右边,得到:ai ^ aii = 0,即 ai = aii
- */
-
-
- int main()
- {
- int n;
- int res = 0;
-
- cin >> n;
- while (n--)
- {
- int x;
- cin >> x;
-
- res ^= x;
- }
-
- if (res != 0)
- {
- cout << "Yes" << endl;
- }
- else
- {
- cout << "No" << endl;
- }
-
- return 0;
- }
- #include <iostream>
-
- using namespace std;
-
-
- // http://t.csdn.cn/3Dks1
-
-
- int main()
- {
- int n;
- int res = 0;
-
- cin >> n;
-
- for (int i = 1; i <= n; i++)
- {
- int x;
- cin >> x;
-
- if (n % 2 == 1)
- {
- res ^= x;
- }
- }
-
- if (res != 0)
- {
- cout << "Yes" << endl;
- }
- else
- {
- cout << "No" << endl;
- }
-
- return 0;
- }
- #include <iostream>
- #include <unordered_set>
-
- using namespace std;
-
- /*
- 给定n堆石子以及一个由k个不同正整数构成的数字集合S。
- 现在有两位玩家轮流操作,每次操作可以从任意一堆石子中拿取石子,每次拿取的石子数量必须包含于集合S,最后无法进行操作的人视为失败。
- 问如果两人都采用最优策略,先手是否必胜。
- 输入格式
- 第一行包含整数k,表示数字集合S中数字的个数。
- 第二行包含k个整数,其中第i个整数表示数字集合S中的第i个数si。
- 第三行包含整数n。
- 第四行包含n个整数,其中第i个整数表示第i堆石子的数量hi。
- 输出格式
- 如果先手方必胜,则输出"Yes"。
- 否则,输出"No"。
- 数据范围
- 1≤n,k≤100,
- 1≤si,hi≤10000
- */
-
- const int M = 10010; // 集合中元素最大有多大、石堆中石子的数量
- const int N = 110; //集合有多少个元素、有多少个石堆
-
- int k, n;
- int s[N]; // 存储集合中元素元素值
- int sg[M]; // 存储每个值的SG值
-
-
-
- /*
- mex运算:设S表示一个非负整数集合。定义mex(S)为求出不属于集合S的最小非负整数的运算,即:mex(S) = min(x), x属于自然数,且x不属于S
- 如 mex({3, 4, 5}) = 0
- */
- int mex(unordered_set<int>& S)
- {
- for (int i = 0;; i++)
- {
- if (S.count(i) == 0)
- {
- return i;
- }
- }
- }
-
-
-
-
-
-
- /*
- SG函数
- 在有向图游戏中,对于每个节点x,设从x出发共有k条有向边,分别到达节点y1,y2,..yk,
- 定义 SG(x)为x的后继节点y1,y2,....yk的SG函数值构成的集合再执行mex(S)运算的结果即:SG(x) = mex((SG(y1), SG(y2). .., SG(yk)))
- 定义终点状态的SG为0
- 特别地,整个有向图游戏G的SG函数值被定义为有向图游戏起点s的SG函数值,即SG(G) = SG(s)。
- 设G1、G2、……、Gm为m个有向图游戏,它的行动规则是任选某个有向图游戏Gi,并在Gi上行动一步。(在本题中,每个石堆对应一个有向图,要求出每个石堆起点的SG值)
- G被称为有向图G1、G2、……、Gm的和。有向图游戏的和的SG函数值等于它包含的各个子游戏SG函数值(起点s的SG函数值)的异或和
- 即:SG(G) = SG(G1) ^ SG(G2) ^ …… ^ SG(Gm)
- 定理: 有向图游戏的某个局面必胜,当且仅当该局面对应节点的SG函数值大于0。
- 有向图游戏的某个局面必败,当且仅当该局面对应节点的SG函数值等于0。
- */
- int SG(int x)
- {
- // 如果 x值 的SG值被算过,直接返回
- // 通过sg数组,避免重复运算
- if (sg[x] != -1)
- {
- return sg[x];
- }
-
-
- // x值的SG值没有被算过,根据定义计算
- unordered_set<int> S;
-
- for (int i = 0; i < k; i++)
- {
- // 如果元素小于等于x才可以计算
- if (s[i] <= x)
- {
- // (x - s[i]) 大于等于 0,需要插入它的SG值
- if (sg[x - s[i]] != -1)
- {
- // 如果(x - s[i])的SG值已经被算过,直接插入
- S.insert(sg[x - s[i]]);
- }
- else
- {
- // 未被算过,计算后插入
- S.insert(SG(x - s[i]));
- }
- }
- }
-
- // 将计算出来 x的SG值 赋给数组并返回
- sg[x] = mex(S);
-
- return sg[x];
- }
-
-
-
- int main()
- {
- cin >> k; // 集合中元素个数
- for (int i = 0; i < k; i++)
- {
- cin >> s[i];
- }
-
- memset(sg, -1, sizeof(sg)); // 如果 sg[i] 为-1,则说明 数i 的 SG值 还未被算过
- cin >> n; // 石堆数
-
- int res = 0; // 存储结果值
- for (int i = 0; i < n; i++)
- {
- int x; // 当前石堆中石子数
- cin >> x;
- res ^= SG(x);
- }
-
- if (res != 0)
- {
- cout << "Yes" << endl;
- }
- else
- {
- cout << "No" << endl;
- }
-
- return 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。