题目链接:https://projecteuler.net/problem=306
题意:两个人在玩游戏,一个 1 ∗ n 1*n 1∗n的纸条,每次可以将连续的2个格子染色,如果一个人无法继续操作就输了,问 1 ≤ n ≤ 1000000 1 \le n \le 1000000 1≤n≤1000000中有多少个 n n n使得先手有必胜策略
题解:
注意到染色之后左右独立
易知
s
g
[
i
]
=
m
e
x
(
s
g
[
j
−
1
]
,
s
g
[
i
−
j
−
1
]
)
sg[i]=mex(sg[j-1],sg[i-j-1])
sg[i]=mex(sg[j−1],sg[i−j−1]) j为枚举的染色位置
然后将sg函数打出来
发现:
1~n:
除了第1、2行,其余的开始循环
根据这个可以写出一个模拟的循环程序
a
1
[
]
、
a
2
[
]
a1[] 、a2[]
a1[]、a2[]的第一个是为了控制下标
// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() puts("okkkkkkkk")
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
typedef long long LL;
const int inf = 1 << 29;
const int a1[]={23333,0,1,1,2,0,3,1,1,0,3,3,2,2,4,0,5,2,2,3,3,0,1,1,3,0,2,1,1,0,4,5,2,7,4,0};
const int a2[]={23333,1,1,2,0,3,1,1,0,3,3,2,2,4,4,5,5,2,3,3,0,1,1,3,0,2,1,1,0,4,5,3,7,4,8};
int main(){
int res=0;
int n;scanf("%d",&n);
for(int i=1;i<=min(n,35);i++)if(a1[i])++res;
if(n>35){
int j=36;
while(j<=n){
for(int i=1;i<=34;i++){
if(a2[i])++res;
++j;
if(j>n){
break;
}
}
}
}
printf("%d\n",res);
return 0;
}
易知
n
=
1000000
n=1000000
n=1000000 时
a
n
s
=
852938
ans=852938
ans=852938
可以bonus:
n
≤
7
e
6
n \le 7e6
n≤7e6 也可以这样做