BZOJ 3576: [Hnoi2014]江南乐 (SG函数)
阅读原文时间:2024年06月03日阅读:1

题意

有nnn堆石子,给定FFF,每次操作可以把一堆石子数不小于FFF的石子平均分配成若干堆(堆数>1>1>1).

平均分配即指分出来的石子数中最大值减最小值不超过111.不能进行操作就算输.询问先手是否有必胜策略.

分析

因为每一推石子实际上是独立的.于是就可以求出每一堆石子的SGSGSG函数后再异或起来.

于是看看怎么求SG(N)SG(N)SG(N).方法是枚举分成的堆数iii.因为最大−-−最小<=1<=1<=1,那么只可能有两种取值.设:

  • 分出来的较小值为rrr, 较大值就是r+1r+1r+1

    分出来的较大值的堆数为kkk, 较小值的堆数就是i−ki-ki−k

那么有 r=N/ir=N/ir=N/i, k=N%ik=N\%ik=N%i. 那么只要把SG(r)SG(r)SG(r)异或(i−k)(i-k)(i−k)次,把SG(r+1)SG(r+1)SG(r+1)异或kkk次.就能得到一个后继状态的SG值SG值SG值.所以把所有的算出来的值取mexmexmex就得到了SG(N)SG(N)SG(N).这里mexmexmex指没有出现过的最小非负整数值.因为异或偶数次相当于没有异或,所以判断一下kkk或者(i−k)(i-k)(i−k)是否为奇数然后异或一次就行了.

但是这样的时间复杂度是O(N2)O(N^2)O(N2)的,所以我们需要优化.

优化方法类似于莫比乌斯反演中所用到的整除分块.我们对于rrr值相等的iii一起处理.假设我们在处理一个区间[i,j][i,j][i,j],满足ri=ri+1=…=rj=rr_i=r_{i+1}=…=r_j=rri​=ri+1​=…=rj​=r.那么从kik_iki​到kjk_jkj​一定是每一次减少rrr,因为相当于在kkk个较大值中选出rrr个,每一个取走111,组成一堆新的石子.

这个时候我们可以发现kik_iki​的奇偶性一定与ki+2k_{i+2}ki+2​相同,且(i−ki)(i-k_i)(i−ki​)的奇偶性也与(i+2−ki+2)(i+2-k_{i+2})(i+2−ki+2​)相同,所以说分成iii堆和分成i+2i+2i+2推石子得到的SGSGSG值是一样的.那么[i+2,j][i+2,j][i+2,j]的值都对mexmexmex没有贡献,所以我们只需要算iii和i+1i+1i+1的值就行了.那么时间复杂度就被降到了O(nn)O(n\sqrt n)O(nn​).

CODE

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
namespace READ {
    inline void read(int &num) {
        char ch; while((ch=getchar())<'0'||ch>'9');
        for(num=0;ch>='0'&&ch<='9';num=num*10+ch-'0',ch=getchar());
    }
}
using namespace READ;
const int MAXN = 100005;
int T, F, SG[MAXN], vis[MAXN];
inline int sg(int N) { //记忆化搜索,预处理是卡着时限过的,而记忆化搜索会快很多
    if(~SG[N]) return SG[N];
    if(N < F) return SG[N]=0;
    int now, r, k;
    for(int i = 2, j; i <= N; i = j+1) { //先把要用到的sg求出来,否则vis数组可能会被重复标号出错
        j = N/(N/i);                     //(但貌似这道题不会WA)
        r = N/i, k = N%i;
        if(k&1) sg(r+1);
        if((i-k)&1) sg(r);
        if(j > i) {
            r = N/(i+1), k = N%(i+1);
            if(k&1) sg(r+1);
            if(((i+1)-k)&1) sg(r);
        }
    }
    for(int i = 2, j; i <= N; i = j+1) {
        j = N/(N/i);
        r = N/i, k = N%i;
        now = 0;
        if(k&1) now ^= SG[r+1];
        if((i-k)&1) now ^= SG[r];
        vis[now] = N;
        if(j > i) { //可能区间长度只有1
            r = N/(i+1), k = N%(i+1);
            now = 0;
            if(k&1) now ^= SG[r+1];
            if(((i+1)-k)&1) now ^= SG[r];
            vis[now] = N;
        }
    }
    int mex = 0;
    for(; vis[mex] == N; ++mex);
    return SG[N]=mex;
}
int main () {
    memset(SG, -1, sizeof SG);
    read(T), read(F);
    while(T--) {
        int ans = 0, x, n; read(n);
        while(n--)
            read(x), ans ^= sg(x);
        putchar(ans ? '1' : '0');
        if(T) putchar(' '); //注意格式
    }
}

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章