来源是ipsc2006的Kruskal
想了挺久,应该是博弈这一块还不太熟。
一个显然的想法是对每个数找最近素数,然后做。
lowpm(x)表示x-(不小于x的最大素数)
特判了其他情况,只剩下n>=2,每个lowpm(x)>=(k+1),我们认为lowpm(x)为这堆石子的个数。转化为一个类似取石子模型(巴什博奕(Bash Game))。
一个很好的性质是谁取完之后如果一堆的个数<=k,那么他就输了。于是把每堆减去k+1,即转化为谁取完谁赢的经典模型,然后按Multi-SG做(关于Multi-SG可以看http://wenku.baidu.com/view/1716d543a8956bec0975e3f4.html)。
Bug有两处
1.特判时没有注意和正常情况的差别,导致(lowpm(x-1)+1)%(m+1)!=0写成了
(lowpm(x))%(m+1)!=0
2.没读玩直接break。。。
还是要加油!
#include <iostream> #include <cstring> #include <algorithm> #include <cmath> #include <cstdio> using namespace std; typedef long long LL; #define rep(i,n) for (LL i=1;i<=n;i++) #define repb(i,b,n) for (LL i=b,n__=n;i<=n__;i++) const LL mN=210,primup=(1<<16); LL a[mN]; LL sa; LL pm[primup],tpm=0; LL n,m; LL lowpm(LL x) { LL now=x; bool can; while (1) { can=true; for (LL i=1;i<=tpm;i++) if (pm[i]*pm[i]<=now) { if (pm[i]==now) break; else if (now%pm[i]==0) { can=false; break; } } else break; if (can) return x-now; now--; } // while (1) // { // x=(2*x+1)/2; // } return 0; } void outans(bool x) { if (x) cout<<"YES"<<endl; else cout<<"NO"<<endl; } int main() { LL ta; repb(i,2,primup) { bool can=true; repb(j,2,LL(sqrt(i)+2)) if (j!=i && i%j==0) { can=false; break; } if (can) { pm[++tpm]=i; } } cin>>ta; rep(tz,ta) { cin>>n>>m; sa=0; bool rev=0; if (n==1) { LL x; cin>>x; if (x>2 && (lowpm(x-1)+1)%(m+1)!=0) { outans(1); } else outans(0); continue; } bool nodo=false; rep(i,n) { LL x; cin>>x; if (nodo) continue; if (x==1) rev=!rev; else { a[++sa]=lowpm(x); if (a[sa]==0) { nodo=true; outans(1); // break; } } } if (!nodo) { LL xo=0; rep(i,sa) { if (a[i]<=m) { nodo=true; outans(1); break; } a[i]-=(m+1); xo^=(a[i]%(m+1)); } if (!nodo) { outans(((rev?1:0) ^ xo)>0); } } } return 0; }