【bzoj3576】【HNOI2014】江南乐

Description

有n堆石子,每堆石子有a[i]个。
每次操作可以把某一堆不少于F个的石子堆分成m份(m>=2且m<=a[i]),并且尽量均分。即最多的石子堆和最少的最多相差1.
不能操作者输,求先手是否有必胜策略。
数据组数≤100,n≤100,F,a[i]≤10^5。

Solution

组合游戏。a[i]^2的sg转移还是挺显然的。
那么我们考虑如何优化。
我们发现,我们 nm 只有 n 中可能的取值。
那么我们可以直接分块(分块大法好)
因为我们只关注石子的奇偶性,所以对于同样的取值中我们分成m份和分成m+2份是不会改变每堆石子的奇偶性的。(即不会改变sg值,证明自行脑补)
那么我们只需要堆每个块处理first和first+1就好了。(分块大法好)
加上记忆化,时间复杂度 O(NN)

Code

#include
#include
#include
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 100000
using namespace std;
int sg[N+5],mex[N+5],n,x,ty,f,id,ans;
bool bz[N+5];
int get(int x) {
    if (xreturn 0;
    if (bz[x]==1) return sg[x];bz[x]=1;
    for(int i=2;i<=x;i=x/(x/i)+1) fo(j,i,min(i+1,x)) 
    get(x/j),get(x/j+1);id++;
    for(int i=2;i<=x;i=x/(x/i)+1) fo(j,i,min(i+1,x)) {
        int nim=0;
        if (x%j%2) nim^=sg[x/j+1];
        if ((j-x%j)%2) nim^=sg[x/j];
        mex[nim]=id;
    }
    for(sg[x]=0;mex[sg[x]]==id;sg[x]++);
    return sg[x];
}
int main() {
    scanf("%d%d",&ty,&f);
    while (ty--){ 
        scanf("%d",&n);
        ans=0;
        fo(i,1,n) scanf("%d",&x),ans^=get(x);
        if (ans) printf("1");else printf("0");
        if (ty) printf(" ");
    }
}

Ps:bzoj真神奇,坑了我4发PE。。。
6666666

你可能感兴趣的:(博弈论,分块)