洛谷2148(SDOI2009) E&D

题目:https://www.luogu.org/problemnew/show/P2148

SG函数+找规律。

普通地用SG函数做。

每两堆是一个独立问题。因为虽然有可能一个人在同一组里连续操作2次,但操作一次一定会把一个必败状态改为必胜状态,不会需要连续操作两次。

关键是怎么快速求SG函数。

打表找规律:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int sg[25][25];
void dfs(int x,int y)
{
    if(sg[x][y]!=-1)return;
    int sum=0;
    for(int i=1;i<x;i++)
    {
        dfs(i,x-i);
        sum|=(1<<sg[i][x-i]);
    }
    for(int i=1;i<y;i++)
    {
        dfs(i,y-i);
        sum|=(1<<sg[i][y-i]);
    }
    for(int i=0;i<=20;i++)
        if((sum&(1<<i))==0)
        {
            sg[x][y]=i;sg[y][x]=i;
            return;
        }
}
int main()
{
    memset(sg,-1,sizeof sg);
    sg[1][1]=0;
    for(int i=1;i<=20;i++)
    {
        for(int j=1;j<=20;j++)
        {
            dfs(i,j);
            printf("%3d",sg[i][j]);
        }
        printf("\n");
    }
    return 0;
}
View Code

然后发现 i 和 j 的规律:

  SG=1:i % 2 ==1 && j % 2 ==1;

  SG=2:i % 4 == 1,2 && j % 4 == 1,2;

  SG=3:i % 8 == 1,2,3,4 && j % 8 == 1,2,3,4;

  ……

所以有了那个log的算法。

仔细一看,那个就是求 i 和 j 的第一个公共0在第几位,所以又有了O(1)的式子。

但是那个O(1)的式子有一个点过不去,是把NO输出成YES,不知何故。

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const ll N=2e4+5;
ll T,n,a[2],sum;
ll sg(ll a,ll b)
{
    for(ll i=0,tmp=2;;i++,tmp<<=1)
        if((a-1)%tmp<(tmp>>1)&&(b-1)%tmp<(tmp>>1))
            return i;
//    ll k=((a-1)|(b-1));
//    k=((k+1)&(-k-1));
//    return k-1;
}
int main()
{
    scanf("%lld",&T);
    while(T--)
    {
        scanf("%lld",&n);sum=0;
        for(ll i=1;i<=n;i++)
        {
            scanf("%lld",&a[i&1]);
            if(!(i&1))sum^=sg(a[0],a[1]);
        }
        if(sum)printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Narh/p/9085607.html