Atcoder ABC 368 F Dividing Game 博弈论题解

此题算是 s g sg sg 函数的基本应用,前置知识(本人之前的文章):博弈论 (SG函数)

题面


在这里插入图片描述
n n n 堆石子,每次可以将一堆石子的石子数量变成它的约数(不能是它自己),轮流操作,直到一方无法操作。

转化

当一堆石子数量是 x x x,它的可以转移到的状态是 y ( x % y = 0 , x ≠ y ) y(x\%y=0,x\ne y) y(x%y=0,x=y),遂连一条从 x x x y y y 的有向边。对于每堆石子都建立起一个 DAG,维护 s g sg sg 函数,初始值 s g 1 = 0 sg_1=0 sg1=0(这里并不需要真的建边,按照拓扑序排序,因为 y < x y<x y<x,只要 i i i 按从小到大的顺序枚举,每一次花 O ( i ) O(\sqrt{i}) O(i ) 的时间遍历 i i i 的所有约数就好)。把这些石子的初始状况的 s g sg sg 函数值异或起来,如果是 0 0 0,Bruno 赢;非 0 0 0,Anna 赢。

代码

#include<bits/stdc++.h>
using namespace std;
int n,a,sg[100005],ans,maxx;
bool flag[100005];
//flag记录这个数在a数组里是否出现奇数次,偶数次的话把偶数个相等的sg异或起来是0,对答案无影响
vector<int> res;//记录邻居中有哪些sg值,方便算MEX 
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a);
		flag[a]=!flag[a];
		maxx=max(maxx,a);
	}
	for(int i=2;i<=maxx;i++){
		res.clear();
		for(int j=1;j*j<=i;j++){
			if(i%j) continue;
			res.push_back(sg[j]);
			if(j!=1&&j*j!=i) res.push_back(sg[i/j]);
		}
		sort(res.begin(),res.end());
		int si=res.size();
		for(int j=0,pos=0;j<=maxx,pos<si;j++){
			while(pos<si&&res[pos]<j) pos++;
			if(pos>=si||pos<si&&res[pos]!=j){
				sg[i]=j;
				break;
			}
		}
//		cout<<i<<' '<<sg[i]<<endl;
		if(flag[i]) ans=ans^sg[i]; 
	}
	if(ans) printf("Anna\n");
	else printf("Bruno\n");
	return 0;
}
  • 22
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值