HDU 3032 + (2020 CCPC网络赛)HDU6892 (SG模板打表找规律)

SG在补充一下。
有向无环图的顶点上定义Sprague-Garundy函数。
Sprague-Grundy定理(SG定理):游戏和的SG函数等于各个子游戏SG函数的Nim和(异或和)。
Sprague-Grundy函数:
定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。
sg(x)=mex{ sg(y) | y是x的后继状态的一种},借助了nim博弈的思想。
下面例题作为理解以及板子。

题目:HDU3032
题意:n堆石子,两个人轮流取石子,选取一堆,至少为1,或者将一堆石子拆成两堆,不能操作的失败。

利用打表找到SG[x]状态,如果拆分SG[X]=SG[X1]^SG[X2]。
sg[0]=0,sg[1]=1,sg[2]=mex{sg{1,1}=sg[1]^sg[1] , sg[0], sg[1] }=2,依次类推…
详见代码,最下面是打出的规律,4为周期。最后异或起来为0即先手必败。
Alice先取。

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
typedef double ld;
typedef pair<ll,ll> PP;
//f[]:可以取走的石子个数
//sg[]:0~n的SG函数值
//mex[]:mex{}
/*int f[10100],sg[10010],mex[10010];
void getSG(int n)
{
    int i,j;
    memset(sg,0,sizeof(sg));
    sg[0]=0;
    for(i=1;i<=n;i++)
    {
        memset(mex,0,sizeof(mex));
        for(j=1;j<=i;j++)
            mex[sg[i-j]]=1;
        for(j=1;j<=i/2;++j) {
            int ans=0;
            ans^=sg[j]; ans^=sg[i-j];
            mex[ans]=1;
        }
        for(j=0;j<=n;j++)    //求mex{}中未出现的最小的非负整数
        {
            if(mex[j]==0)
            {
                sg[i]=j;
                break;
            }
        }
    }
}*/
//注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍
//n是集合s的大小 S[i]是定义的特殊取法规则的数组
/*int s[110],sg[10010],n;
int SG_dfs(int x)
{
    int i;
    if(sg[x]!=-1)
        return sg[x];
    bool vis[110];
    memset(vis,0,sizeof(vis));
    for(i=1;i<=x;i++)
    {
        if(sg[x-i]==-1) {
            sg[x-i]=SG_dfs(x-i);
        }
        vis[sg[x-i]]=1;
    }
    for(int i=1;i<=x/2;++i) {
        if(sg[i]==-1)
            SG_dfs(i);
        if(sg[x-i]==-1)
            SG_dfs(x-i);
        int temp=0;
        temp^=sg[i]^sg[x-i];
        vis[temp]=1;
    }
    int e;
    for(i=0;;i++)
        if(!vis[i])
        {
            e=i;
            break;
        }
    return sg[x]=e;
}*/

int main() {
    int T;
    scanf("%d",&T);
    while(T--) {
        int n; int ans=0; scanf("%d",&n);
        for(int i=1;i<=n;++i) {
            int x;scanf("%d",&x);
            if(x%4==0) {
                ans^=(x-1);
            }
            else if(x%4==3) {
                ans^=(x+1);
            }
            else ans^=x;
        }
        if(ans) printf("Alice\n");
        else printf("Bob\n");
    }
    return 0;
}
/*
1::::: 1
2::::: 2
3::::: 4
4::::: 3
5::::: 5
6::::: 6
7::::: 8
8::::: 7
9::::: 9
10::::: 10
11::::: 12
12::::: 11
13::::: 13
14::::: 14
15::::: 16
16::::: 15
*/

题目:HDU6892
题意:有n堆,每次可以将一堆分成, 分 成 L k 的 长 度 有 k 堆 , k ∣ L , k 至 少 为 2 分成\frac{L}{k}的长度有k堆,k|L,k至少为2 kLk,kLk2,谁最后取1了,谁输掉比赛。

由于k是L的因子分堆,后继的状态在范围内,我们可以先用SG函数找找规律。(详见代码)

可以发现2次幂的贡献全部是1,其余的数字很小,可以发现是素因子的指数的和。
注意一下时间,和一些细节。

#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<istream>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define llinf 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
typedef double ld;
typedef pair<ll,ll> PP;
const int MAX=1e5+2;
/*int sg[10010];
int SG_dfs(int x)
{
    int qaq=x;
    if(sg[x]!=-1)
        return sg[x];
    map<int,int>vis;
    vector<int>hh;
    for(int i=2;i*i<=x;++i) {
        if(x%i==0) {
            hh.push_back(i);
            if(i*i!=x) {
                hh.push_back(x/i);
            }
        }
    }
    hh.push_back(x);
    sort(hh.begin(),hh.end());
    for(int i=0;i<hh.size();++i) {
        int v=hh[i];
        if(sg[x/hh[i]]==-1) {
            SG_dfs(x/hh[i]);
        }
        if(v&1)
         vis[sg[x/hh[i]]]=1;
        else
        vis[0]=1;
    }
    int e;
    for(int i=0;;i++)
        if(!vis[i]) {
            e=i;
            break;
        }
    return sg[qaq]=e;
}
int main() {

    memset(sg,-1,sizeof(sg));
    sg[0]=sg[1]=0;
    for(int i=1;i<=100;++i) {
        printf("%d:::::%d\n",i,SG_dfs(i));
    }
    return 0;
}*/
int tot=0; bool vis[MAX]; int prime[9595];
void init()
{
    memset(vis,false,sizeof(vis));
    vis[1]=vis[0]=true;
    for(int i=2;i<MAX;i++)
    {
        if(!vis[i])
        {
            prime[tot++]=i;
        }
        for(int j=0;j<tot;j++)
        {
            if(i*prime[j]>=MAX)
                break;
            vis[prime[j]*i]=true;
            if(i%prime[j]==0)
                break;
        }
    }
}
int main() {
    init();
    int T;
    scanf("%d",&T);
    while(T--) {
        int n;
        scanf("%d",&n);
        if(n==1) {
            int x; scanf("%d",&x);
            if(x==1) {
                printf("L\n");
            }
            else printf("W\n"); continue;
        }
        int ans=0;
        for(int i=1;i<=n;++i) {
            int x; scanf("%d",&x);
            if(x<MAX) {
                if(!vis[x]) {
                    ans^=1;
                    continue;
                }
            }
            int sum=0;
            if(!(x&1)) {
                ++sum;
                while(x%2==0) {
                    x/=2;
                }
            }
            if(x==1) {
                ans^=sum;
                continue;
            }
            for(int j=1;j<tot&&prime[j]*prime[j]<=x;++j) {
                if(x%prime[j]==0) {
                    while(x%prime[j]==0) {
                        x/=prime[j]; ++sum;
                    }
                }
            }
            if(x>1) ++sum;
            ans^=sum;
        }
        if(ans) printf("W\n");
            else printf("L\n");
    }
    return 0;
}

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页