题解 P1857 【质数取石子】
Polaris_Lorna · · 题解
这题是一道非常复杂的博弈论题
首先 通过近似线性筛的筛法 预处理出所有的质数
我们设SG函数为此游戏胜负的状态 sg[i]=0为负 1为正 表示当石子数=i时,当前操作的人必败/胜
用一个队列对所有sg函数进行预处理 所有必败状态加上一个质数必然为必胜状态 因为当石子数=必败状态的石子数+质数的时候,当前操作的人仅需取走这个质数的石子便可以获胜
别以为知道这些就能A掉了
电脑不像你想象的那么沙雕 每当电脑觉得他要输的时候 电脑会想方设法拖延你获胜的步伐 同理 当你要赢的时候 你总是想快一点胜利
那么最重要的一点要来了
我们需要在适当的时候取最大或最小值
枚举所有位置和当前位置可扩展的位置,我们且令f[x]为石子数=x时的最小步数
如果扩展到的位置没被扩展过,那么当前位置就给他一个初始值=f[l]+1 如果!!!上一个状态为必胜状态并且当前状态为必胜状态,那么扩展了这个节点毫无意义,直接跳过这个点;
如果上一个扩展而来的状态是必胜状态,当前点却是必败状态,如果你是人,你怕判断这个吗???直接-1就可以了,关键是要考虑对手的感受,对手不愿意让你输,他会竭尽所能找你麻烦,步数要取max啦。如果上一个扩展而来的状态是必败状态,当前点却是必胜状态。
同样想一想,如果你是你的对手,你要赢了,你怕判断这个吗???直接-1掉了,但是你是你,你要速战速决,你要用最少的步数解决这个无聊的游戏,所以在这里f去的是min。到这里最关键的地方就结束了。
好了O(1)的查询 此题AC了(本人写了七个小时)蒟蒻勿喷
#include <bits/stdc++.h>
#define maxn 20000
using namespace std;
int f[20010];
int prime[20010];
int pd[20010];
int q[1000010];
int sg[20010];
int hsh[20010];
int lose[100010];
int n,x,l,r,standard=0,cnt=0,last,losenum;
inline void build_prime()
{
pd[1]=1;
for (int i=2;i<=maxn;i++)
{
int j=i;
if (pd[i]) continue;
while (j<=maxn)
{
j+=i;
pd[j]=1;
}
}
for (int i=2;i<=maxn;i++)
{
if (!pd[i])
prime[++cnt]=i;
}
}
void prepare()
{
while (l<=r)
{
for (int i=1;i<=cnt;i++)
{
int next=prime[i]+q[l];
if(next>maxn) continue;
int last=q[l];
if (!sg[q[l]])
sg[next]=1;
}
if (l==r) {
for (int i=last+1;i<=maxn;i++)
{
if (sg[i]==0)
{
last=i;
lose[i]=1;
q[++r]=i;
break;
}
}
}
l++;
}
}
inline void dp()
{
while (l<=maxn)
{
for (int i=1;i<=cnt;i++)
{
int next=prime[i]+l;
int lasti=l;
int num=f[l]+1;
if (next>maxn) continue;
if (sg[l]&&sg[next]) continue;
if (!lose[next]&&lose[l])
{
if (!f[next]) f[next]=num;
else f[next]=min(f[next],num);
}
if (lose[next]&!lose[l])
{
if (!f[next]) f[next]=num;
else f[next]=max(f[next],num);
}
l++;
}
}
int main()
{
memset(hsh,0,sizeof(hsh));
memset(pd,0,sizeof(pd));
build_prime();
l=1;r=2;
memset(q,0,sizeof(q));
memset(sg,0,sizeof(sg));
//sg[i]=1 win sg[i]=0 lose
for (int i=1;i<=20009;i++)
{
f[i]=standard;
}
sg[0]=0;sg[1]=0;sg[2]=1;sg[3]=1;
q[1]=0;q[2]=1;
f[0]=0;f[1]=0;
hsh[0]=1;hsh[1]=1;
last=1;
memset(lose,0,sizeof(lose));
prepare();
lose[1]=1;lose[0]=1;//其实lose数组没什么必要
//就是sg函数为0的情况
memset(q,0,sizeof(q));
q[1]=0;q[2]=1;
f[0]=0;f[1]=0;
l=0;r=2;
dp();
cin>>n;
for (int i=1;i<=n;i++)
{
cin>>x;
if (sg[x]==0) cout<< -1 <<endl;
else cout<<f[x]<<endl;
}
}