【洛谷P3235】江南乐
题目
题目链接:https://www.luogu.com.cn/problem/P3235
小A是一个名副其实的狂热的回合制游戏玩家。在获得了许多回合制游戏的世界级奖项之后,小A有一天突然想起了他小时候在江南玩过的一个回合制游戏。
游戏的规则是这样的,首先给定一个数F,然后游戏系统会产生T组游戏。每一组游戏包含N堆石子,小A和他的对手轮流操作。每次操作时,操作者先选定一个不小于2的正整数M (M是操作者自行选定的,而且每次操作时可不一样),然后将任意一堆数量不小于F的石子分成M堆,并且满足这M堆石子中石子数最多的一堆至多比石子数最少的一堆多1(即分的尽量平均,事实上按照这样的分石子万法,选定M和一堆石子后,它分出来的状态是固定的)。当一个玩家不能操作的时候,也就是当每一堆石子的数量都严格小于F时,他就输掉。(补充:先手从N堆石子中选择一堆数量不小于F的石子分成M堆后,此时共有N+M-1)堆石子,接下来小A从这N+M-1堆石子中选择一堆数量不小于F的石子,依此类推。
小A从小就是个有风度的男生,他邀请他的对手作为先手。小A现在想要知道,面对给定的一组游戏,而且他的对手也和他一样聪明绝顶的话,究竟谁能够获得胜利?
思路
题面直接从洛谷复制过来懒得修 LaTeX 了。
显然每一堆石子之间是独立的,考虑预处理出 \(1\sim 10^5\) 个石子时的 \(sg\),然后就可以 \(O(\sum n)\) 回答了。
从小到大枚举石子个数,对于确定的石子数量 \(i\),分成 \(j\) 组时会变成 \(j-i\bmod j\) 堆有 \(\lfloor \frac{i}{j} \rfloor\) 个石子的堆,\(i\bmod j\) 堆有 \(\lfloor \frac{i}{j} \rfloor+1\) 个石子的堆。
发现石子数量和 \(\lfloor \frac{i}{j} \rfloor\) 有关,考虑整除分块,假设分成 \(l\sim r\) 堆时,石子数都是 \(\lfloor \frac{i}{l} \rfloor\) 和 \(\lfloor \frac{i}{l} \rfloor+1\)。
假设分成 \(l\) 组时有 \(a\) 个 \(\lfloor \frac{i}{l} \rfloor\),\(b\) 个 \(\lfloor \frac{i}{l} \rfloor+1\)。那么分成 \(l+1\) 组时可以理解为将 \(\lfloor \frac{i}{l} \rfloor\) 个石子数量为 \(\lfloor \frac{i}{l} \rfloor+1\) 的石子堆中分别取出一个石子,然后扔到新的一堆中。
这个操作实质上为当 \(l\) 是偶数时,\(a\) 加上了偶数,\(b\) 减去了偶数;当 \(l\) 是奇数时,\(a\) 加上了奇数,\(b\) 加上了偶数。
由于我们在计算一种后继状态时只关心所有后继游戏 \(sg\) 的异或和,所以如果 \(a,b\) 都改变都是偶数,那么 \(sg\) 值不变,否则会异或上 \(sg[\lfloor \frac{i}{l} \rfloor]\) 和 \(sg[\lfloor \frac{i}{l} \rfloor+1]\)。
而因为异或两次相同的数等价于没有异或,所以我们对于一组 \([l,r]\),我们只需要计算 \(l\) 和 \(l+1\) 的贡献即可。
那么时间复杂度为 \(O(m\sqrt{m}+\sum n)\),其中 \(m=10^5\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int Q,n,m,ans,sg[N];
bool vis[N];
int main()
{
scanf("%d%d",&Q,&m);
for (int i=m;i<=100000;i++)
{
memset(vis,0,sizeof(vis));
for (int l=2,r;l<=i;l=r+1)
{
r=i/(i/l);
for (int j=l;j<=min(r,l+1);j++)
{
int s=0;
if ((j-i%j)&1) s^=sg[i/j];
if ((i%j)&1) s^=sg[i/j+1];
vis[s]=1;
}
}
for (int j=0;vis[j];j++)
sg[i]=j+1;
}
while (Q--)
{
scanf("%d",&n);
ans=0;
for (int i=1,x;i<=n;i++)
{
scanf("%d",&x);
ans^=sg[x];
}
printf("%d ",(bool)ans);
}
return 0;
}