BZOJ 3576: [Hnoi2014]江南乐【SG函数】

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/C20181220_xiang_m_y/article/details/88595579

题目传送门

题目分析:

每堆石子是独立的,可以求出每堆的SG函数再异或起来。
所以考虑预处理(或者记忆化搜索)SG函数。
设石子数量为 n n ,枚举它分成的堆数 i i ,那么较小堆的石子数是 n i \lfloor\frac ni\rfloor (较大堆是 n i \lfloor\frac ni\rfloor +1),设 n / i n/i 的余数是 k k ,那么大堆的数量是 k k ,小堆的数量是 i k i-k ,根据 k k i k i-k 的奇偶性进行异或得到分成 i i 堆的SG值,在vis数组中标记一下就行了。

但是这样做是 O ( n 2 ) O(n^2) 的,注意到 n i \lfloor\frac ni\rfloor 只有 n \sqrt n 个取值(画一个反比例函数根据对称性就看得出来),考虑整除分块优化。

n i = n i + 1 \lfloor\frac ni\rfloor=\lfloor\frac n{i+1}\rfloor ,大小堆的石子数是一样的,那么多出来的一堆肯定是从 k k 个大堆里面选 n i \lfloor\frac ni\rfloor 堆,每堆取出一个石子放到一起形成一个新堆,对应的 k = n i + = n i + 1 k-=\lfloor\frac ni\rfloor,小堆+=\lfloor\frac ni\rfloor+1 ,由于是根据奇偶性进行异或并标记vis数组,那么 n i \lfloor\frac ni\rfloor 相等的一段最多只会有i和i+1两种不同的贡献,i+2的大小堆堆数的奇偶性与i相同,所以一段只需要算两次就可以了。
复杂度 O ( n n ) O(n\sqrt n)

Code:

#include<cstdio>
#include<cctype>
#define maxn 100005
inline void read(int &a){
    char c;while(!isdigit(c=getchar()));
    for(a=c-'0';isdigit(c=getchar());a=a*10+c-'0');
}
int SG[maxn],F,T,n,vis[maxn];
int calc(int k){
    int x,y,s;
    for(int i=2,j;i<=k;i=j+1){
        s=k/i;
        y=k%i,x=i-y;
        vis[((x&1)*SG[s])^((y&1)*SG[s+1])]=k;
        j=k/s;
        if(i<j) y=k%(i+1),x=i+1-y,vis[((x&1)*SG[s])^((y&1)*SG[s+1])]=k;
    }
    x=0;while(vis[x]==k) x++;
    return x;
}
void Pre(){for(int i=F;i<=100000;i++) SG[i]=calc(i);}
int main()
{
    read(T),read(F);
    Pre();
    while(T--){
        read(n);int x=0,y;
        while(n--) read(y),x^=SG[y];
        printf("%d%c",x?1:0,T?32:10);
    }
}

猜你喜欢

转载自blog.csdn.net/C20181220_xiang_m_y/article/details/88595579