您的位置:首页 > 其它

BZOJ 3576: [Hnoi2014]江南乐

2016-04-12 11:13 281 查看
首先把游戏拆解一下会发现每一堆石子是一个游戏

所以是Multi-SG,总体sg值是每个游戏的sg值的异或和

考虑单个游戏,数量为n的一堆石子的后继状态是拆完之后的石子的sg值的异或和

于是结合SG定理就得到了一个优秀的m^2算法(优秀个毛线啊)(m为石子个数的最大值)

然后我们考虑一个比较特殊的情况

显然把n拆成m堆会出现k=n/m和k+1两种情况,堆数分别是k1=m-k2,k2=n%m,所以这个的sg值是((k1&1)*sg[k])^((k2&1)*sg[k+1])

假设把n拆成i堆和j堆得出的k是一样的,那么只用考虑k1和k2就好了

显然k2确定的时候k1已经确定了,那么只用考虑k2就好了

另外会发现k2的奇偶只有两种情况,分别搞一下就好了(就是说在某个k相同的区间内这个sg值只有两种可能)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=100000+5;
int sg
,vis
,f;
void build(){
for(int n=f;n<=100000;n++){
for(int i=2,last;i<=n;i=last+1){
int k=n/i;
last=n/k;
int k2=n%i,k1=i-k2;
vis[sg[(k1&1)*k]^sg[(k2&1)*(k+1)]]=n;
if(i+1<=min(n,last)){
k2=n%(i+1),k1=i+1-k2;
vis[sg[(k1&1)*k]^sg[(k2&1)*(k+1)]]=n;
}
}
for(int i=0;;i++)
if(vis[i]!=n){
sg
=i;
break;
}
}
}
int main(){
//freopen("a.in","r",stdin);
int T;scanf("%d%d",&T,&f);
build();
while(T--){
int n;scanf("%d",&n);
int ans=0;
for(int i=1;i<=n;i++){
int x;scanf("%d",&x);
ans^=sg[x];
}
printf("%d",ans?1:0);
if(T)putchar(' ');
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: