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;
}
View Code

 

posted @ 2016-03-16 09:50  诚叙  阅读(452)  评论(0编辑  收藏  举报