[SG函数 + 分块] BZOJ4035: [HAOI2015]数组游戏

博弈好题。这种博弈的01取反的模型可以把白色看做有奇数个石子,黑色看做偶数个,因为同一位置偶数个石子SG值异或会抵消…..
这么理解的话,可以把一个石子,即一个白块看做一个独立的游戏。
现在只需求SG值。

SG(i)=mex{0,SG(2i),SG(2i) ^ SG(3i),SG(2i) ^ SG(3i) ^ SG(4i),...}

一般SG函数的题可以先打表找规律,发现连续 ni 为定值的一段SG值是相等的,自己yy一下就发现很对。
有了这个性质就很简单了,暴力枚举块,枚举后继状态时连续的一段可以 O(1) …… 种复杂度大概是远小于 O(nn) 的一个东西…

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=500005;
int n,Q,ans,b[maxn],c[2][maxn],vis[maxn],clk,sqrt_n;
inline int getSG(int x){
    x=n/(n/x);
    if(x<=sqrt_n) return c[0][x]; else return c[1][n/x];
}
void Solve(){
    for(int i=1;i<=n;i=n/(n/i)+1) b[++b[0]]=n/(n/i);
    for(int i=b[0];i>=1;i--){
        int x=b[i],now=0; clk++; vis[0]=clk; 
        for(int j=x+x;j<=n;){
            int t=(n/(n/j))/x*x, cnt=(t-j)/x+1;
            vis[now^getSG(j)]=clk;
            if(cnt&1) now^=getSG(j);
            j=t+x;
        }
        int res=0; while(vis[res]==clk) res++;
        if(x<=sqrt_n) c[0][x]=res; else c[1][n/x]=res;
    }
}
int main(){
    freopen("bzoj4035.in","r",stdin);
    freopen("bzoj4035.out","w",stdout);
    scanf("%d",&n); sqrt_n=sqrt(n)+1;
    Solve();
    scanf("%d",&Q);
    while(Q--){
        int t,x; scanf("%d",&t); ans=0;
        while(t--) scanf("%d",&x), ans^=getSG(x);
        if(ans) puts("Yes"); else puts("No");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值