题解 P3235【[HNOI2014]江南乐】

$\text{Link}$

题意

一次操作可以将一堆数量为 $i$($i\ge F$)的石子分为 $j-i\bmod j$ 堆数量为 $\left\lfloor\frac{i}{j}\right\rfloor$ 的石子和 $i\bmod j$ 堆数量为 $\left\lceil\frac{i}{j}\right\rceil$ 的石子,其中 $j$ 是每次操作时玩家自行选定,满足 $2\le j\le i$,$F$ 为给定。不能操作者输。

初始有 $n$ 堆石子,第 $i$ 堆石子的数量为 $a_i$。多组数据,但 $F$ 不改变。

$T,n\le100$,$F,a_i\le10^5$.

思路

我们定一个数的 $\text{SG}$ 值为 $f(x)$,一个局面的 $\text{SG}$ 值为 $f(state)=\bigoplus_{i\in state}f(i)$。

于是求出每个数的 $\text{SG}$ 值即可。

首先,对于 $i<F$,$f(i)=0$,因为显然此时无法取到石子,先手必败。

然后枚举 $j$,我们可以算出此时划分的局面的 $\text{SG}$ 值。因为只有两种棋子的取值,且知道每堆的数量,所以算此时划分的局面的 $\text{SG}$ 值是 $O(1)$ 的。

令 $v=\max\{a_i\}$,则此时时间复杂度为 $O(v^2+Tn)$,无法通过。

瓶颈在于计算 $\text{SG}$,我们观察式子,发现有 $\left\lfloor\frac{i}{j}\right\rfloor$ 这一形式,于是考虑整除分块。

但是式子里还有 $i\bmod j$ 这一形态,整除分块不好处理,怎么办呢?

考虑向奇偶性方向想,

  • 当 $2\nmid\left\lfloor\frac{i}{j}\right\rfloor$ 时,$j-i\bmod j=j-(i-j\left\lfloor\frac{i}{j}\right\rfloor)=j(1+\left\lfloor\frac{i}{j}\right\rfloor)-i$,发现此时 $j-i\bmod j$ 的奇偶性不变,则此部分贡献不变;
  • 当 $2|\left\lfloor\frac{i}{j}\right\rfloor$ 时,$i\bmod j=i-j\left\lfloor\frac{i}{j}\right\rfloor$,则 $i\bmod j$ 奇偶性不变,此部分贡献不变。

发现无论 $\left\lfloor\frac{i}{j}\right\rfloor$ 的奇偶性,两种数量的石子的堆数中总有一种不变。

于是我们只需要枚举 $2$ 个 $j$ 值就能代表所有情况了。

求 $\text{mex}$ 可以暴力,因为集合里的数的个数为 $O(\sqrt v)\times O(1)=O(\sqrt v)$。

此时时间复杂度降为 $O(v\sqrt v+Tn)$,可以通过。

由于查询的 $a_i$ 值较少,不会涵盖整个值域,可以使用记忆化搜索,比递推快很多。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff

}
const int N=1e5+10;
int n,T,f;
int SG[N];
inline int getSG(int i){
    if(i<f) return 0;
    if(~SG[i]) return SG[i];
    bool vis[100];
    memset(vis,0,sizeof(vis));
    for(int j=2,r;j<=i;j=r+1){
        r=i/(i/j);
        int res=0;
        bool flag=i%j;
        if(i%j&1) res^=getSG(i/j+1);
        if(j-i%j&1) res^=getSG(i/j);
        j++;
        vis[res]=i;
        if(flag){
            res=0;
            if(i%j&1) res^=getSG(i/j+1);
            if(j-i%j&1) res^=getSG(i/j);
            j++;
            vis[res]=i;
        }
    }
    for(int j=0;;j++)
        if(!vis[j])
            return SG[i]=j;
}
int main(){
    memset(SG,-1,sizeof(SG));
    T=read(),f=read();
    while(T--){
        n=read();
        int res=0;
        for(int i=1;i<=n;i++){
            res^=getSG(read());
        }
        write(res>0),putc(' ');
    } 
    flush();
}

再见 qwq~

posted @ 2021-07-07 08:51  ffffyc  阅读(8)  评论(0)    收藏  举报  来源