P2252
提供一种不产生精度误差的做法。
本题解中所有数字以及未知数均为整数。
本题解中假定输入的
我们对先手必败态进行打表,表如下:
0 0
1 2
3 5
4 7
6 10
8 13
9 15
11 18
12 20
14 23
......
考虑相邻两个必败态之间较小的数的差距,打表得:
12122121221221212212122122121221221212212122122121221212212212122122121221212212212122122121221212212212122......
不难发现,相邻两项之间较小的数的差距只能是
证明如下:
基本规则1:(0,0) 是必败态。
基本规则2:若 (a,b) 是必败态,则 (a+x,b) 、(a,b+x) 、(a+x,b+x) 均为必胜态。(x 为正整数)
基本规则3:若一个状态 (a,b) ,任何 (a-x,b) 满足 x\le a 、(a,b-x) 满足 x\le b 、(a-x,b-x) 满足 x\le \min(a,b) 均已定义且均为必胜态,则该状态为必败态。(x 为正整数)
不难发现若
引理1:对于每个 a ,至多存在一个 b 使得 (a,b) 是必败态
证明:若存在
引理2:对于每个 a ,存在一个 b 使得 (a,b) 是必败态
证明:若不存在
根据引理1,至多有
引理3:对于任意必败态 (a,b) 满足 a\le b ,对于任意 0\le x<b-a ,存在唯一必败态 (m,n) 满足 m<a,n-m=x ,且对于任意必败态 (m,n) 满足 m<a,m\le n ,(m,n) 满足 n-m\le b-a ,且对于任意必败态 (a,b),(m,n) 满足 a\le b,m\le n,a\le m ,满足 b\le n 。
证明:引理3对于
若引理3对于任意
否则,根据引理2,存在唯一必败态
必然存在必败态
则
引理3的推论:对于
总证明:
根据引理3的推论,不存在必败态
则对于任意集合
对于
则对于
证明完毕。
经过这一番骚操作之后,我们证明了上文中的数列只存在
接下来就是找规律并证明的时间了。
不难发现,数列中第一项是
不难发现,数列中的 12122121221221212212122122...,也就是说构成它本身!
接下来我们证明这两点。
证明数列中的 1 从不连续出现。
根据数列中只存在
经检验,第一个
如果数列中有连续的
那么就必然存在
证明数列是以如上所述方式自定义的。
以下文字将会采用不那么严谨的表述方式。
考虑原数列中的每个
也就是说每一个
再进行一次类似的操作,每一个
也就是说,每一个
考察导出前后的数列:
导出前:12122121221221212212122122...。
导出后:122121221221212212122122...。
不难发现导出后比导出前少了开头的 12。
那么第一个 1 对应了缺少的 12,其余的每个 1 和它前面的 2 必定会导出 21212,这个 1 就对应了中间的 12,而每个 2 和前面的数字要么导出了 12212(前面是 1),要么导出了 212212(前面是 2),也就是说,每一个 2 又恰好可以对应一个 122。
那么有了这些结论,我们已经可以
不难发现,如果数列以
证明上述结论。
每一个
所以如果
特判
现在问题简化成了计算数列的第
如果不能快速求得这个答案的话我们就前功尽弃了。
很幸运,这个数列是自定义的,也就是说,它的各个部分会有一定程度的相似性。
如下图所示。
因为这个数列是自定义的,可以用如上方式思考。
“组成部分的长度”实际上是斐波那契数列,可以方便地用数学归纳法证明。
比如我们要求数列的第
最后一定会转化为求数列第
求排名呢?
可以预处理每层的两个段中各有几个
时间复杂度:
参考代码:
#include<cstdio>
using namespace std;
long long x,y,t,f[100]={0,1,1},s[100]={0,0,1},g[100]={0,1},i,num1,num2;
main()
{
scanf("%lld%lld",&x,&y);
if(x>y)
{
t=x;
x=y;
y=t;
}
for(i=3;i<=50;i++)
{
f[i]=f[i-1]+f[i-2];
s[i]=s[i-1]+s[i-2];
g[i]=g[i-1]+g[i-2];
}
if(y==0)
printf("0");
else if(x==0)
printf("1");
else
{
for(i=1;i<=50;i=i+2)
if(f[i]+f[i+1]>x)
break;
for(;i>=3;i=i-2)
{
if(x>=f[i]&&x<f[i]*2)
{
num1=num1+s[i];
x=x-f[i];
}
else if(x>=f[i]*2)
{
num1=num1+s[i]+s[i-1];
x=x-f[i]-f[i-1];
}
}
if(x==0)
{
printf("1");
return 0;
}
for(i=1;i<=50;i=i+2)
if(f[i]+f[i+1]>y)
break;
for(;i>=3;i=i-2)
{
if(y>=f[i]&&y<f[i]*2)
{
num2=num2+g[i];
y=y-f[i];
}
else if(y>=f[i]*2)
{
num2=num2+g[i]+g[i-1];
y=y-f[i]-f[i-1];
}
}
if(y==1||num1!=num2-1)
printf("1");
else
printf("0");
}
}