【HNOI2014】江南乐

题面

题解

知识引入 - \(SG\)函数

任何一个公平组合游戏都可以通过把每个局面看成一个顶点,对每个局面和它的子局面连一条有向边来抽象成这个“有向图游戏”。下面我们就在有向无环图的顶点上定义Sprague-Grundy函数。

定义\(mex\)运算,表示最小的不属于这个集合的非负整数

如:\(mex(\{0,1,2,4\})=3,mex(\{1,3,5\})=0,mex(\{\})=0\)

对于一个给定的有向无环图,定义关于图的每个顶点的Sprague-Grundy函数\(g\)如下:
\[ g(x)=mex(\{g(y)\mid y \in \mathrm{suc}_x\}) \]
\(g(x)\)的性质可以得出:\(g(x) = 0 \Leftrightarrow x \in\)必败态

如果一个游戏可以分成多个子游戏,那么整个游戏的\(SG\)值就是每个子游戏的\(SG\)值的异或和。

本题题解

部分分可以暴力求\(g(x)\)

枚举分成的堆数。如果将\(x\)分成了\(i\)堆,那么这\(i\)堆中有\(x \% i\)\(\left\lceil\frac{x}{i}\right\rceil\),有\(i - x \% i\)\(\left\lfloor\frac{x}{i}\right\rfloor\)

对于每一个\(i\),算出它的\(SG\)值,为所有分出来的\(SG\)值的异或和的\(mex\)

然后\(SG\)函数可以记忆化。

接下来继续推性质,因为\(x \oplus x = 0\),所以只需要根据奇偶性讨论一下就可以了,这时候大约有\(70\)分。

然后\(\left\lfloor\frac{x}{i}\right\rfloor\)可以数论分块,于是数论分块即可。

代码

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define RG register

inline int read()
{
    int data = 0, w = 1; char ch = getchar();
    while(ch != '-' && (!isdigit(ch))) ch = getchar();
    if(ch == '-') w = -1, ch = getchar();
    while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar();
    return data * w;
}

const int maxn(100010);
int sg[maxn], vis[maxn], T, F;

int SG(int x)
{
    if(x < F) return 0;
    if(~sg[x]) return sg[x];
    for(RG int l = 2, r; l <= x; l = r + 1)
    {
        r = (x / (x / l));
        for(RG int j = l; j <= std::min(l + 1, r); j++)
        {
            int a = x % j, b = x / j, c = j - x % j, s = 0;
            if(a & 1) s ^= SG(b + 1);
            if(c & 1) s ^= SG(b);
            vis[s] = x;
        }
    }

    for(RG int i = 0; ; i++) if(vis[i] != x) return sg[x] = i;
}

int main()
{
    memset(sg, -1, sizeof sg);
    T = read(), F = read();
    while(T--)
    {
        int n = read(), ans = 0;
        for(RG int i = 1; i <= n; i++) ans ^= SG(read());
        printf("%d ", (bool)ans);
    }
    return 0;
}

转载于:https://www.cnblogs.com/cj-xxz/p/10404446.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值