题解 P3235【[HNOI2014]江南乐】
题意
一次操作可以将一堆数量为 ()的石子分为 堆数量为 的石子和 堆数量为 的石子,其中 是每次操作时玩家自行选定,满足 , 为给定。不能操作者输。
初始有 堆石子,第 堆石子的数量为 。多组数据,但 不改变。
,.
思路
我们定一个数的 值为 ,一个局面的 值为 。
于是求出每个数的 值即可。
首先,对于 ,,因为显然此时无法取到石子,先手必败。
然后枚举 ,我们可以算出此时划分的局面的 值。因为只有两种棋子的取值,且知道每堆的数量,所以算此时划分的局面的 值是 的。
令 ,则此时时间复杂度为 ,无法通过。
瓶颈在于计算 ,我们观察式子,发现有 这一形式,于是考虑整除分块。
但是式子里还有 这一形态,整除分块不好处理,怎么办呢?
考虑向奇偶性方向想,
- 当 时,,发现此时 的奇偶性不变,则此部分贡献不变;
- 当 时,,则 奇偶性不变,此部分贡献不变。
发现无论 的奇偶性,两种数量的石子的堆数中总有一种不变。
于是我们只需要枚举 个 值就能代表所有情况了。
求 可以暴力,因为集合里的数的个数为 。
此时时间复杂度降为 ,可以通过。
由于查询的 值较少,不会涵盖整个值域,可以使用记忆化搜索,比递推快很多。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff
}
const int N=1e5+10;
int n,T,f;
int SG[N];
inline int getSG(int i){
if(i<f) return 0;
if(~SG[i]) return SG[i];
bool vis[100];
memset(vis,0,sizeof(vis));
for(int j=2,r;j<=i;j=r+1){
r=i/(i/j);
int res=0;
bool flag=i%j;
if(i%j&1) res^=getSG(i/j+1);
if(j-i%j&1) res^=getSG(i/j);
j++;
vis[res]=i;
if(flag){
res=0;
if(i%j&1) res^=getSG(i/j+1);
if(j-i%j&1) res^=getSG(i/j);
j++;
vis[res]=i;
}
}
for(int j=0;;j++)
if(!vis[j])
return SG[i]=j;
}
int main(){
memset(SG,-1,sizeof(SG));
T=read(),f=read();
while(T--){
n=read();
int res=0;
for(int i=1;i<=n;i++){
res^=getSG(read());
}
write(res>0),putc(' ');
}
flush();
}
再见 qwq~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通