BZOJ1874: [BeiJing2009 WinterCamp]取石子游戏
AC链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1874
首先开头点歌 Eason 《1874》。多么有意义的题目编号啊...
唔,这题感觉和经典的NIM取石子还是扯不开关系啊...
有了上一题的经验,我们知道了SG函数是可以递推求的,整个的状态是可以由一些小状态异或相加的。
然后怎么递推呢?当然就是根据后继状态来咯...
笔者此时还没有想起可以用时间戳这种方法来弄一个桶。
所以思考了一会儿桶要开多大:
发现至多会有x个后继状态,那么桶中从0开始连续的一段就最多连续x下,所以只要开x+1这么大就可以了,不过>x要记得判掉。(可是如果所有人的x相同那就找到k使得2^(k-1)<=x<=2^k,那么最大的数也只能达到2^k)
不过我这题还是开了1000,毕竟复杂度很充足...
好吧,时间戳就是常见的多次使用一个桶不用清空的那个神奇trick了,你们一定会的。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=11; const int maxz=1010; int n,m; int a[maxn],b[maxn]; int SG[maxz]; bool T[maxz]; int main(){ #ifndef ONLINE_JUDGE freopen("1874.in","r",stdin); freopen("1874.out","w",stdout); #endif int Max=0,ans=0; scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); if(Max<a[i]) Max=a[i]; } scanf("%d",&m); for(int i=1;i<=m;i++) scanf("%d",&b[i]); for(int i=1;i<=Max;i++){ memset(T,0,sizeof(T)); for(int j=1;j<=m;j++) if(i>=b[j]) T[SG[i-b[j]]]=true; for(int j=0;j<maxz;j++) if(!T[j]) {SG[i]=j;break;} } for(int i=1;i<=n;i++) ans^=SG[a[i]]; if(!ans) {puts("NO");return 0;} for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++) if(a[i]>=b[j]) if((ans^SG[a[i]-b[j]]^SG[a[i]])==0){ puts("YES"); printf("%d %d",i,b[j]); return 0; } } return 0; }