[HNOI2007]分裂游戏 题解
中考+研学+放假 7 天,历经 18 天狂暴颓废后第一次碰 OI 的第一道题
SG 定理
Statement
给定 堆石头,每次可以选择一个堆,减去其中一个,然后使得两个编号所选择堆分别增加一个石头。
轮流操作,不能操作者输
求必胜方案数和字典序最小的第一步方案
Solution
naive 想法:
前 30 min 经典读错题,没有看到 的限制。
然后也很蠢,想到这个游戏可能会无限进行下去(无限? 莫名其妙想到说,哈,那肯定是形成了一个环
乱 gb 想了一下如何在有向有环图中做 SG ,发现不太刑
仔细想想发现石子总和肯定是不断增加的,所以不存在环,只能是说无限长的链
那么干脆约定一下不能有这种反哺的情况,也就是默认反哺会进入一个必胜态
然后回头一看,很好,经典读错题,没事,太久没碰 OI 了,可以理解
(但是现在不能理解的是为什么这么久都不碰 OI ,也许现在正是考验我快速恢复能力的时候)
然后发现这个状态数吓人,模拟之后发现不会处理
正解:参考 lhm 的题解
仔细分析整个过程,考虑使用 SG 定理分解游戏
考虑每一个单独的石子颠沛流离最终到达位置 n 的过程,产生了无数的子子孙孙
我们可以直接认为对这些子子孙孙进行的操作依旧是对原来放置在 x 位置的石头的博弈,容易发现每一颗石头的博弈过程是相互独立的
所以我们直接设 表示在位置 x 的一个石头的博弈状态即可
容易发现可以对原问题中每一个位置的石头数量
SG 直接 即可
Code
#include<bits/stdc++.h>
using namespace std;
const int N = 25;
char buf[1<<23],*p1=buf,*p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read(){
int s=0,w=1; char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))s=s*10+(ch^48),ch=getchar();
return s*w;
}
int sg[N],a[N];
int T,n,ans,cnt,fg;
int dfs(int n){
if(sg[n]!=-1)return sg[n];
bool vis[5005]={0};
for(int i=1;i<n;++i)
for(int j=1;j<=i;++j)
vis[dfs(i)^dfs(j)]=1;
for(int i=0;i<5005;++i)if(!vis[i])
return sg[n]=i;
}
signed main(){
memset(sg,-1,sizeof(sg));
sg[1]=0,dfs(22),T=read();
while(T--){
n=read(),ans=cnt=fg=0;
for(int i=1;i<=n;++i){
a[i]=read();
if(a[i]%2)ans^=sg[n-i+1];
}
if(!ans){
puts("-1 -1 -1\n0");
continue;
}
for(int i=1;i<=n;++i)if(a[i])
for(int j=i+1;j<=n;++j)for(int k=j;k<=n;++k)
if((ans^sg[n-i+1]^sg[n-j+1]^sg[n-k+1])==0){
cnt++;
if(!fg)
printf("%d %d %d\n",i-1,j-1,k-1),fg=1;
}
printf("%d\n",cnt);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!