zoukankan      html  css  js  c++  java
  • [BZOJ 3576] 江南乐

    Link:

    BZOJ 3576 传送门

    Solution:

    算是发现博弈论题目的一部分套路了吧

    求SG函数,然后用各种奥妙重重的方式降求解SG的复杂度

    此题由于每一组独立,用SG函数肯定是没问题的。

    先看暴力 $O(n^2)$ 求解SG的方式:

    枚举每个$i$分成的份数$m$,只分为$i/m$和$i/m+1$两类

    由于异或的自反性只要确定这两类数的奇偶性便确定$sg[i/m]$或$sg[i/m+1]$是否计入$sg[i]$

    观察 $O(n^2)$ 的方法,发现很多$i$的$i/m$和$i/m+1$都相同

    由于多个相同的$sg[i']$是对$sg[i]$不具有贡献的,于是我们想到分组计算

    有以下两条性质:

    (1)可以证明,对于确定的$i$和$m$,$i/m$的数字个数不超过$2*log(i)$

    ($m<sqrt(i)$时最多$log(i)$个,$sqrt(i)<m<i$时也最多$log(i)$个)

    (2)$i/m == i/(m+2)$时$SG$值相同

    ($imod m$和$m-{imod m}$的奇偶性保持不变)

    于是我们分组计算,且每次只计算每组的前两个$SG$值即可

    由于F不变,用记忆化搜索提高速度

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int MAXN=1e5+10;
    int T,F,n,t,dat[MAXN],sg[MAXN],mex[MAXN],cnt=0;
    bool vis[MAXN];
    
    int get_sg(int x)
    {
        if(x<F) return 0;
        if(vis[x]) return sg[x];
        vis[x]=true;  //一定要先将vis置位,否则会死循环
        for(int i=2;i<=x;i=x/(x/i)+1)
            for(int j=i;j<=min(i+1,x);j++)
                get_sg(x/j),get_sg(x/j+1);
        cnt++;
        
        for(int i=2;i<=x;i=x/(x/i)+1)
            for(int j=i;j<=min(i+1,x);j++)
            {
                int nim=0;
                if(x%j%2) nim^=sg[x/j+1];
                if((j-x%j)%2) nim^=sg[x/j];
                mex[nim]=cnt;
            }
        sg[x]=0;
        while(mex[sg[x]]==cnt) sg[x]++;
        return sg[x];
    }
    
    int main()
    {
        scanf("%d%d",&T,&F);
        
        while(T--)
        {
            scanf("%d",&n);int res=0;        
            
            for(int i=1;i<=n;i++) 
                scanf("%d",&t),res^=get_sg(t);
            if(res) putchar('1');
            else putchar('0');
            if(T) putchar(' ');
         }
        return 0;
    }

    Review:

    1、按$i/m$分块的套路

    算是第二次遇见这样的套路了(BZOJ 2301)

    再遇到对$i/m$有处理的题目,根据其值的种类不超过$2*log(n)$的性质分块处理即可

    注意其中转移到下一块的处理技巧:$i=x/(x/i)+1$

    个人认为$x/(x/i)$表示根据当前$x/i$的值找出最大的$i'$,再加一就是另一个$x/i$值了

    2、由于计算$SG$时相同的前继状态是没有贡献的,

    考虑如何消去$SG$相同的前继状态

    3、只要有数据能重复利用,使用记忆化搜索

    4、求SG函数时,初始值最好都设为0,防止异或-1这种情况

    如果使用$vis$数组也一定要第一时间更新,防止陷入死循环

  • 相关阅读:
    [CF666E] Forensic Examination
    [BZOJ3739] DZY loves math VIII
    [BZOJ3561] DZY Loves Math VI
    VS中的类模板
    php中的this,self,parent
    js中的 Table 对象
    CEF中弹出窗口的处理
    VC禁止或允许拖拽改变窗口尺寸
    MFC系统自动生成的停靠窗格关掉后,如何重新显示?
    VS2010 如何自动生成UML图
  • 原文地址:https://www.cnblogs.com/newera/p/9119501.html
Copyright © 2011-2022 走看看