题解 P3179 【[HAOI2015]数组游戏】

xzyxzy

2018-07-31 19:50:46

Solution

~~折腾一晚上终于搞出来了~~ ### Step1 首先是翻棋子问题的套路,每个棋子可以作为单个游戏,整个游戏就是所有白棋子的SG异或和,可以想到一个暴力算SG的方法,有50分 ### Step2 进一步优化就是可以打表发现$\frac{n}{i}$下取整的值相同的棋子SG值相同(i表示位置),于是有一个分块想法 先处理$q[i]=k$表示从左到右第i块的右端点为k,然后我们枚举x=q[i],同时记录res为异或和,cnt表示有多少不同的SG值,于是我们对于区间[L,R]([L,R]的$\frac{n}{i}$值相等),我们需要由下面的状态取mex转移 $$\{0,SG([2L,2R]),SG([2L,2R]\ xor\ SG[3L,3R]....)\}$$ 于是我们接下来对[l,r]分块([l,r]表示[L,R]的[l,r]倍在同一个大块里面),用这个的奇偶性来维护SG异或的前缀和 最后一个优化是存储x的值,块x表示右端点x,$\frac{n}{i}$,那么如果x在1到根号n只之内直接存,否则存$\frac{n}{i}$,这样子就能优化空间到根号n了 ```cpp #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> using namespace std; const int N=500000; int T,n,W,c[N],a[N],b[N]; int q[N],nn,tag[N],tot; void Pre() { nn=sqrt(n);tot=0; for(int l=1,r;l<=n;l=r+1) r=n/(n/l),q[++tot]=r; for(int u=tot;u;u--) { int x=q[u],res=0,cnt=0; for(int l=2*x,r;l<=n;l=r+x) { r=n/(n/l)/x*x; int z=r>nn?b[n/r]:a[r]; c[++cnt]=res^z;tag[c[cnt]]=1; if(((r-l)/x+1)&1) res^=z; } res=1;while(tag[res]) res++; x>nn?b[n/x]=res:a[x]=res; for(int i=1;i<=cnt;i++) tag[c[i]]=0; } } void work() { cin>>W;int res=0; for(int i=1;i<=W;i++) { int x;cin>>x; res^=x>nn?b[n/x]:a[x]; } res?puts("Yes"):puts("No"); } int main() { cin>>n>>T;Pre(); while(T--) work(); } ``` 打个小广告:[博弈总结](https://www.cnblogs.com/xzyxzy/p/9397756.html) 管理员请重新审核之前有个地方有误`o=o`