【BZOJ4035】数组游戏,博弈论+分块乱搞

传送门
思路:
起初思路挺好,后来就比较奇怪的一道题目
玩法很像SDOI2016R2的硬币游戏,不过那个题目直接暴力SG就可以过,本题直接套规则只有30分;
稍微优化一下,不枚举k,直接每次xor一下后继状态的sg,可以得50分,复杂度 O(nlogn)
70分需要一个比较好玩的结论:
如果 ni=nj ,那么 sg(i)=sg(j)
我们可以归纳证明一下:
ni=1 时,显然 sg(i)=1 ,因为它只能令自身反色
ni>1 时, sg(i) n2i,n3i ..的sg值决定,又 nij=nij ,所以 ni 相同的位置后继状态相同,sg值也就相同了
然后就可以分块求了, ni 的取值最多有 2n 种,转移时考虑落在当前区间内的后继点奇偶就可以了,复杂度 O(n) ,可以得70分,个人以为这正是本题巧妙的地方
接下来的优化就比较玄学了,本人一开始并不知道怎么做,弃疗后百度了题解,好像是什么跑不满的小常数 O(n) ,还有什么玄学/hash,表示不是很懂,不过里面所说的合并sg给了我启发,因为有很多 ni 不相等的块sg值相同,这个优化比较奇怪但确实很好用,在 n=109 时用这个优化,块的数量只有9223个,所以我们只要求出一个块的sg后和前一个块比较,如果sg相同就合并,查找时二分一下就可以了
提示:本题中 sg 函数都不大
代码:

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
int n,m,q,cnt;
int pos[64005],f[64005];
struct node{
    int l,r,sg;
}da[64005];
bool vis[1000];
void dp()
{
    for (int i=pos[0];i;--i)
    {
        int t=0,mx=0,p=cnt;
        for (int last=2,j=2;j<=n/pos[i];j=last+1)
        {
            for (;j*pos[i]>da[p].r&&p;--p);
            if (p) last=da[p].r/pos[i];
            vis[t^da[p].sg]=1;
            mx=max(mx,t^da[p].sg);
            if (last-j+1&1) t^=da[p].sg;
            vis[t]=1,mx=max(mx,t);
        }
        for (int j=1;;++j)
            if (!vis[j])
            {
                f[i]=da[++cnt].sg=j;
                if (cnt>1&&da[cnt].sg==da[cnt-1].sg)
                    da[--cnt].l=pos[i-1]+1;
                else
                    da[cnt].l=pos[i-1]+1,da[cnt].r=pos[i];
                break;
            }
        for (int j=0;j<=mx;++j) vis[j]=0;
    }
}
main()
{
    scanf("%d%d",&n,&q);
    for (int last,i=1;i<=n;i=last+1) pos[++pos[0]]=last=n/(n/i);
    dp();
    for (int x,ans;q;--q)
    {
        ans=0;
        scanf("%d",&m);
        for (;m;--m)
            scanf("%d",&x),
            ans^=f[lower_bound(pos+1,pos+pos[0]+1,n/(n/x))-pos];
        puts(ans?"Yes":"No");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值