欢迎光临
我们一直在努力

博弈论详解 3(SG定理的运用)

时隔一年半,主播再次回到博弈论。这次是练习时间!!!

如果你没有学过SG定理或者想要复习一下

如果没有看过之前的前置文章的可以先看零基础知识讲解。 Episode 1(Nim基础):https://blog.csdn.net/2401_84512298/article/details/141559570?spm=1001.2014.3001.5502 Episode 2(SG定理):https://blog.csdn.net/2401_84512298/article/details/141563982 为了和我一样没有买会员的小伙伴,我把之前的文章设成公开了。

简单复盘

每个状态都有一个 SG 函数值,比如在基本的取石子问题中,一堆有

x

x

x 个石子就是一种状态。没有石子(零个)的 SG 值就是

0

0

0(即必输的局面)。求的方法是通过状态的转移方式建立出一个以状态为节点,转移为单向边的 DAG。比如

5

5

5 个石子就可以取掉两个,转移到

3

3

3 个石子,所以

5

5

5

3

3

3 有一条单向边。当石子变成

n

n

n 堆的时候,就取每一堆的初始状态的 SG 函数值,然后把它们

x

o

r

xor

xor 一下,如果是

0

0

0 就必输,否则先手胜。

例题(先自己做做看)

原题:Codeforces 2004E A 和 B 面前有

n

n

n 堆石子,第

i

i

i 堆有

a

i

a_i

ai 个。每次可以从

x

x

x 个石子的一堆里面拿走

y

y

y 个,需满足

g

c

d

(

x

,

y

)

=

1

gcd(x,y)=1

gcd(x,y)=1。 当然你不能把石子取成负数。如果到某个人的时候他没办法取石子了,他就输了。 这里

a

i

10

7

,

n

3

10

5

a_i\\le 10^7, \\, n\\le 3\\,·10^5

ai107,n3105

思路

这里的状态和复盘中的状态一模一样,都是这一堆还有几个石子。但是转移不一样了,这次每次可以取走一些特定数字的石子,但仍然是一张有向无环图。只要算出所有状态的 SG 函数,取一下初始的

n

n

n 堆所对应的函数值的

x

o

r

xor

xor 就搞定了。可是如果暴力算出每种石子数的 SG 值,那就 TLE 了,因为第一层循环枚举当前在求的状态(

10

7

10^7

107),第二层循环枚举所有小于当前石子数的值 (

10

7

10^7

107),也就是要取的数量。但是我们二话不说,直接开干,看看 SG 值长什么样子再说。 (这里建议读者自己写一下程序找找规律)

#include<bits/stdc++.h>
#pragma GCC optimize("O3,unroll-loops")
#pragma GCC target("avx2,bmi,bmi2,lzcnt,popcnt")
using namespace std;
#define ll long long
#define endl '\\n'
ll sg[205];
bool have[1005];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
sg[0]=0;
cout<<0<<':'<<sg[0]<<endl;
for(int i=1;i<=200;i++){
memset(have,0,sizeof(have));
for(int j=1;j<=i;j++)
if(__gcd(j,i)==1)
have[sg[ij]]=1;
for(int j=0;j<=1000;j++)
if(!have[j]){
sg[i]=j;
break;
}
cout<<i<<':'<<sg[i]<<endl;
}
return 0;
}

然后我们的输出长这样:(冒号左边为石子数,右边为所对应的 SG 函数值)

在这里插入图片描述 读者此时发现:我的天哪!为什么石子个数是偶数(蓝色)的都是

0

0

0,然后那些质数(橙红色)感觉就像是编号一样,一个一个往上加?(

1

1

1 不是质数,只是加上之后才是从

1

1

1 开始加,比较好看,它刚好顶替了

2

2

2 的位置,

2

2

2 虽然是质数,但也是偶数,所以变成

0

0

0 了)更惊喜的是,主播并不知道为什么。 剩下的规律实在有点难,所以主播偷偷去看了一下题解,然后发现居然就是它的最小质因子的 SG 值。比如

25

25

25 就取

5

5

5 的数值,

3

3

3

77

77

77 就取

7

7

7 的值,

4

4

4。那就很简单了,直接筛选一下质数,然后把 SG 值全算出来就好了。

代码

#include<bits/stdc++.h>
#pragma GCC optimize("O3,unroll-loops")
#pragma GCC target("avx2,bmi,bmi2,lzcnt,popcnt")
using namespace std;
#define ll long long
#define endl '\\n'
int t,n,a[300005],sg[10000005],id,fac[10000005];
bool np[10000005];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
memset(fac,0x3f,sizeof(fac));
sg[1]=1;id=1;
for(int j=2*2;j<=10000000;j+=2) np[j]=1,fac[j]=min(fac[j],2);
for(int i=3;i<=10000000;i++){
if(!np[i]){
sg[i]=++id;
for(int j=i*2;j<=10000000;j+=i)
np[j]=1,fac[j]=min(fac[j],i);
}
}
for(int i=3;i<=10000000;i+=2) if(np[i]) sg[i]=sg[fac[i]];
cin>>t;
while(t){
cin>>n;
int sum=0;
for(int i=1;i<=n;i++){
cin>>a[i];
sum^=sg[a[i]];
}
if(sum==0) cout<<"Bob\\n";
else cout<<"Alice\\n";
}
return 0;
}

习题

https://www.hackerrank.com/contests/5-days-of-game-theory/challenges/a-chessboard-game/problem 英文题解在评论区提供的 USACO Guide 的讲解当中,如果各位想要我写这道题的题解就发评论里,尽量更新。

赞(0)
未经允许不得转载:171主机测评 » 博弈论详解 3(SG定理的运用)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址