bzoj 4035: [HAOI2015]T3 SG函数

来源:互联网 发布:海关数据开发客户 编辑:程序博客网 时间:2024/06/08 05:26

       博弈论神题。。

       首先把问题转化为存在若干白点,然后将所有点翻转成黑点,先全部翻转的为胜。这和原题是等价的,因为如果某一步选一个黑点翻是必胜策略,那么下一步对手可以选择翻同一个点而使局面变回来,因此考虑双方都是采取最好的策略因此不会有一方翻黑点。

       那么就把每一个白点看成是一个子游戏,最后将SG函数全部异或起来即可,由SG定理可知有:

       SG(i)=mex{SG[i*1]^SG[i*2]^...^SG[i*k]},k=[2,N/i]。

       然而N为10^9,因此不能直接递推。注意到实际上某一个SG[i]函数的值只和N/i有关,因此有用的状态只有O(N^0.5)个,然后根据上面的式子递推即可。

       但是还要考虑有用状态的保存问题,注意到有N^0.5个状态的下标<=N^0.5,这部分直接保存即可;另有N^0.5个状态的下标>=N^0.5,但是N/下标<=N^0.5且各不相同,因此用N除之后再保存即可。

       时间复杂度O(N),不过常数较小,可以通过。

AC代码如下:

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#define N 100005using namespace std;int n,m,c[2][N],a[N]; bool bo[N];int nxt(int x,int y){ return (x==y)?y+1:y/(y/(x+1)); }void pfs(){int i,j,now,x,t,cnt;for (i=1; i<=n; i=nxt(i,n)){now=cnt=0;for (j=2; j<=i; j=nxt(j,i)){x=i/j; t=(x>m)?c[1][n/x]:c[0][x];a[++cnt]=now^t; bo[a[cnt]]=1;if ((i/x-i/(x+1))&1) now^=t;}now=1; while (bo[now]) now++;if (i>m) c[1][n/i]=now; else c[0][i]=now;for (j=1; j<=cnt; j++) bo[a[j]]=0;}}int main(){scanf("%d",&n); m=(int)sqrt(n); pfs();int cas,cnt; scanf("%d",&cas);while (cas--){scanf("%d",&cnt); int ans=0,x;while (cnt--){scanf("%d",&x); x=n/x;ans^=(x>m)?c[1][n/x]:c[0][x];}puts((ans)?"Yes":"No");}}


by lych

2016.3.15

0 0