题意:n堆石子,每堆石子有黑和白两种颜色,拿黑色堆石子的时候必须拿数量最少的(最少一个),白色没有限制可以任意拿(最少一个)。后手的人可以在游戏开始之前对所有石子堆染色,问后手能赢的染色方案数。

题解:首先可以将黑色和白色分成两个游戏,两个游戏的异或和为0即后手胜利。

白色游戏:相当于nim游戏

黑色游戏:通过sg打表能发现

SG值 = 最小堆石子数 − ( ( 最小堆数量 + [ 所有堆石子数相同 ] ) % 2 )

能发现sg值只跟最小堆有关系,然后我们可以枚举最小堆。用一个pre维护异或前缀和,suf维护异或后缀和,从后往前维护一个线性基。由于我们枚举到的堆是最小堆,所以石子数量比当前枚举的堆要少的一定是白色,我们要最后总的异或值为0,那么要看线性基里能不能表示出满足异或和为0的数,方案即 2的(s - tot)次方。最后再乘上一个组合数即可。具体细节代码可见。

sg函数打表代码:

#include<bits/stdc++.h>using namespace std;const int N = 110;
int sg[N];int SG(priority_queue<int, vector<int>, greater<int> > q) {bool vis[N] = { false };if (q.empty()) return 0;int t = q.top();q.pop();vis[SG(q)] = true;for (int i = 1; i < t; i++) {auto tmp = q;tmp.push(t - i);vis[SG(tmp)] = true;}for (int i = 0; ; i++)if (!vis[i]) return i;
}int main() {priority_queue<int, vector<int>, greater<int> > q;for (int i = 0; i <= 5; i++) {for (int j = i; j <= 5; j++) {for (int k = j; k <= 5; k++) {for (int l = k; l <= 5; l++) {for (int r = l; r <= 5; r++) {while (!q.empty()) q.pop();if (i) q.push(i), cout << i << " ";if (j) q.push(j), cout << j << " ";if (k) q.push(k), cout << k << " ";if (l) q.push(l), cout << l << " ";if (r) q.push(r), cout << r << " ";cout << " : " << SG(q) << endl;}}}}}return 0;
}

题目代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 1e5 + 10;
int f[N], inv[N];
int a[N], pre[N], suf[N];
int same[N], num[N];
map<int, int> t;
struct LB {const static int N = 64;ll a[N + 1], tot;LB() { memset(a, 0, sizeof(a)), tot = 0; }bool insert(ll x) {for (int i = N; i >= 0; i--) {if (x & (1ll << i)) {if (a[i]) x ^= a[i];else {a[i] = x;tot++;break;}}}return x > 0;}bool count(ll x) {for (int i = N; i >= 0; i--)if (x & (1ll << i)) x ^= a[i];return x == 0;}
}LB;int qpow(int a, int b) {int res = 1;while (b) {if (b & 1) res = res * a % mod;a = a * a % mod;b >>= 1;}return res;
}void init(int n) {f[0] = inv[0] = 1;for (int i = 1; i <= n; i++)f[i] = f[i - 1] * i % mod;inv[n] = qpow(f[n], mod - 2);for (int i = n - 1; i >= 1; i--)inv[i] = inv[i + 1] * (i + 1) % mod;
}int C(int a, int b) {if (a < b) return 0;return f[a] * inv[b] % mod * inv[a - b] % mod;
}signed main() {init(N - 1);int n;cin >> n;int ans = 0;for (int i = 1; i <= n; i++) {cin >> a[i];t[a[i]]++;ans ^= a[i];}ans = (ans == 0 ? 1 : 0);sort(a + 1, a + 1 + n);for (int i = 1; i <= n; i++)pre[i] = pre[i - 1] ^ a[i];for (int i = n; i >= 1; i--)suf[i] = suf[i + 1] ^ a[i];for (int i = n; i >= 1; i--) {same[i] = (a[i] == a[i + 1] ? same[i + 1] + 1 : 1);num[i] = t[a[i]];}int head = n + 1;for (int i = n; i >= 1; i--) {int sum = 0;//记录方案int x = pre[i - 1];//前缀异或和int y = a[i] - ((same[i] + 1) & 1);//当前选了same[i]个数量为a[i]的石堆染成黑色(所有石堆石子数相同)int z = suf[i + same[i]];//后缀异或和if ((x ^ y ^ z) == 0) sum = (sum + 1) % mod;//如果异或和为0,方法数加一y = a[i] - (same[i] & 1);//同上y,但是所有石堆石子数不相同int need = x ^ y;//满足异或和为0的数if (LB.count(need)) sum = (sum + qpow(2, n - head + 1 - LB.tot)) % mod;//如果need能用线性基表示出来,线性基能表示出这个数的方案为2^(s - tot)//s为线性基插入次数,tot为线性基内的个数if ((need ^ suf[i + same[i]]) == 0) sum = (sum - 1 + mod) % mod;//如果后缀所有数异或和等于need那么方案数要减一if (same[i - 1] == 1) {//如果当前是a[i]数量的最后一个石堆,把所有值为a[i]的数插入到线性基中for (int j = 1; j <= same[i]; j++) LB.insert(a[i]);head = i;}ans = (ans + sum * C(num[i], same[i]) % mod) % mod;//最后要乘上在num[i]中选same[i]堆的方法数}cout << ans << endl;return 0;
}

2020CCPC威海 J - Steins;Game (sg函数、线性基)相关推荐

  1. 2020CCPC威海 J.Steins;Game

    题目大意 给 n n n 堆石子,每堆可以是黑或者白.如果想拿黑色堆的石子,必须拿数目最小的那一堆(至少拿 1 1 1 个,至多拿完):白色石子可以任意拿(同样至少拿 1 1 1 个,至多拿完). 后 ...

  2. gym102798 2020CCPC威海J Steins;Game

    题目: 给定 n n n堆石子 a a a,每堆石子被染成了黑色或者白色,现在两个人轮流进行以下的其中一个操作: 1.从石子数量最少的一个黑色石堆中拿走若干石子 2.从任意一个白色石堆中拿走若干石子 ...

  3. 【基础操作】线性基详解

    线性基是一个奇妙的集合(我摘的原话) 这里以非 $OI$ 的角度介绍了线性基 基础部分 模板题 给你 $n$ 个数的集合,让你选出任意多个不重复的数,使得它们的异或和最大. 线性基是什么 我们称集合 ...

  4. CodeForces - 850C Arpa and a game with Mojtaba(博弈+sg函数)

    题目链接:点击查看 题目大意:给出n个数,两个人轮流按照规则操作,不能操作的人即为失败,规则很简单,每次找一个质数p的k次幂,记做x=,将数组中所有包含x倍数的数都除以x,必须保证至少有一个数可以除以 ...

  5. 2020CCPC威海

    2020CCPC威海 2020CCPC威海榜单 题号 题目 知识点 难度 A Golden Spirit 思维,推导题 签到题 B Labyrinth dfs+思维 金牌题 C Rencontre D ...

  6. BZOJ 1874: [BeiJing2009 WinterCamp]取石子游戏(SG函数)

    Time Limit: 5 Sec  Memory Limit: 162 MB Submit: 871  Solved: 365 [Submit][Status][Discuss] Descripti ...

  7. 一类SG函数递推性质的深入分析——2018ACM陕西邀请赛H题

    题目描述 定义一种有根二叉树\(T(n)\)如下: (1)\(T(1)\)是一条长度为\(p\)的链: (2)\(T(2)\)是一条长度为\(q\)的链: (3)\(T(i)\)是一棵二叉树,它的左子 ...

  8. hdu1848(sg函数打表)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1848 题意:中文题诶- 思路:直接sg函数打表就好了 代码: 1 #include <iostr ...

  9. AtCoder AGC043C Giant Graph (图论、SG函数、FWT)

    题目链接 https://atcoder.jp/contests/agc043/tasks/agc043_c 题解 场上感觉没啥思路就放弃了,场下想了十几分钟发现是水题,血亏...(只能怪自己计数水平 ...

最新文章

  1. 工作中一些代码优化的地方
  2. openwrt+linux编译,openwrt x86 编译部署
  3. 绝对和相对误差(absolute relative error)
  4. Java与C++的几个显著不同
  5. mysql高级之子查询,多表查询,外连接,集合操作,内部函数与数据控制_月隐学python第24课
  6. 面象对象设计6大原则之三:里氏替换原则
  7. DDMS工具使用(转)
  8. PaddlePaddle︱开发文档中学习情感分类(CNN、LSTM、双向LSTM)、语义角色标注
  9. spring-session实现分布式集群session的共享(转)
  10. 《线性代数及其应用 第四版》习题1.4
  11. 银保监会:防范以“元宇宙”名义进行的非法集资 | 产业区块链发展周报
  12. Arduino DHT11温湿度传感器数据示例
  13. Python入门基础-七、案例4 52周存钱挑战 #列表(list)#math 库#for循环#range()用法#函数的参数传递#datetime库
  14. 计算机Excel运行环境,Excel2007免费完整版 最新电脑版
  15. 算术平方根的整数部分(简单)*求平方根的三种方法**整数与小数取绝对值*
  16. Nginx 企业级优化
  17. 梁勇(Danniel Liang) java教材例题:java程序购买额按税率求营业税 java中数值保留2位小数的方法...
  18. 企业数字化进程中,商业智能 BI 如何降本增效
  19. C++实现K-means,聚类原理解析(并用在图片像素点聚类)
  20. 括号中的可选、必选表示

热门文章

  1. @Autowired自动装配
  2. 《算法笔记》4.1小节——算法初步->排序 问题 C: EXCEL排序
  3. Android自定义电池电量显示组件(kotlin,java)
  4. 基于微信小程序的高校课堂教学管理系统
  5. Matlab中对字符串的处理(转 备查)
  6. 【Python 08】变量的定义和使用
  7. 主食应为主少吃饭多吃菜是误区
  8. 交通事故中的车辆外观损伤识别
  9. 计算机系统盘涨满怎么办,电脑系统盘满了怎么办【突破攻略】
  10. 51单片机:有趣的定时器实验