[POJ3922]Now解题报告

来源:互联网 发布:vue.js chrome插件 编辑:程序博客网 时间:2024/06/14 01:46

【题目描述】
从过去回来后,你又对未来产生了浓厚兴趣。你去办理时空签证,却被告知能到未来
的人都必须是高智商的,所以需要玩一个游戏来测试智商。
游戏规则如下:
1. 有一堆石子数为 N 的石子堆,两个人轮流取,最后不能取的人输
2. 你是先手,且你不能在第一步将石子全部取完
3. 设上一个人选的石子数为 X,则这次取的石子数不能超过 X*K
你需要知道你是否能胜利,以及如何胜利
【输入格式】
第一行一个正整数 T,表示测试组数
下面的 T 行,每行两个整数,代表 N 和 K
【输出格式】
对于每组数据,输出组数,然后如果先手负,则输出”lose”,否则输出最小的第一步取的
石子数,注意中间有空格
【样例】
Sample Input
5
16 1
11 1
32 2
34 2
19 3
Sample Output
Case 1: lose
Case 2: 1
Case 3: 3
Case 4: lose
Case 5: 4
【备注】
对于 10%的数据,有 N <= 100, K <= 2
对于 30%的数据,有 N <= 10^8, K <= 3
对于 100%的数据,有 2 <= N <= 10^8, K <= 10^5, T <= 200


这个题是一道非常非常好的题,我能做到它真是太幸运了!
为了搞明白其正确性,我参阅了国家集训队论文,发现这道题从2002年便开始出现在集训队论文里,当时它的时间复杂度是O(N3)(09年集训队论文说的),然后在09集训队论文里,它的时间复杂度推进到了O(N);现在它的时间复杂度是O(logK+1KN),可以发现对于这个题这是一个被卡常数的时间复杂度。。但是由于数据水。。


而作为一道神题的题解,网上的题解(估计都是抄的)都TM是一个模子,完全讲不明白。
我在这里给出一个较为严谨的思路和证明(这可是我花了一天时间才搞明白的)。


首先呢,化繁为简,考虑K=1的情况:没有思路?打表!
打出不同的N,给出的最终答案是多少。
发现当N=2i时必败。

然后呢,让我们考虑一下这是为什么。
这显然是与二进制有关的,于是我们转二进制考虑:
减一个数我们把它视为减去若干个2i.
那么我们从高到低减去这些1,如果一个1对应的被减数位是1,那么就直接减掉好了;而如果一个1对应的被减数位是0,那么就相当于是把上一个1的位置变成0,而从那里开始到这一位都变成1.
也就是说,从0减不会让1减小,而取完所有石子的充要条件是石子数的二进制中的1的总数是0.
那么一件有趣的事情就出现了,既然是博弈,那么应该在每次A取完后,B不管如何取,都只能从0位上减;这显然是容易做到的,只要A每次取lowbit即可,而B所减的最低位一定会出现一个1,所以A一定能取到lowbit。而之所以2i不行,就是因为A第一次取不到lowbit,导致情况反转了。
而打表发现,确实是lowbit,也就证实了我们的结论。

考虑我们是如果搞出K=1的情况的,
1、打表。(不要小看打表,这是一个非常非常重要的技巧!)
2、证明出四件事:
①依照某种取法,A每次都能取一个值;而这么做将剩给B一个他不能取到的数,或A把石子全部取走了。
②这个值只要石子数为正,都一定存在。
③这种取法是唯一的。(这虽然是一个看上去没有关系的问题,但是在之后的证明中你会发现这才是这个题真正的重点)
④A每次一定能取到这个值。


然后我们开始考虑K=2的情况。
打表发现,必败态是Fibonacci。
下面我们将说明这个问题。
(在以下的叙述中,我们将认为斐波那契数列以1为首项,2为次项)

先介绍——齐肯多夫引理。
任意一个正整数必然能被表示成斐波那契数列中的若干不连续项(包括一项)之和;
这个定理看起来似乎非常玄妙,但实际上它不过是两个简单事实的叠加而已;虽然有的时候形式上的证明是必不可少的,但是我却认为非形式的证明才能揭示定理的灵魂所在,大段的符号和逻辑转移往往最关键的突破被掩盖。
显然1,2(斐波那契数列中的前两项)可以被用斐波那契数列中的项表示,那么我们不妨设1..i-1,均能用斐波那契数列中的若干项表示;
fj为最大的小于等于i的斐波那契项,即fji<fj+1
fj=i,则i可以被斐波那契数列中的项表示;
fj<i,则fji可以用斐波那契数列中的项表示,而且表示法中最大项<fj1;因为若非如此,则有fj1+fj=fj+1i,这便与前设矛盾了。
综上,原命题成立。
齐肯多夫定理说明了①②,而实际上③④正是齐肯多夫定理的推论。
若干不相邻项实际上就是说任意两项之比大于2,因为fi=fi1+fi2fi1>fi2(首项是1,次项是2),所以fi>2fi2,即斐波那契数列中若干不连续项之比是大于2的。
也就是说我们可以把一个不在斐波那契数列上的整数写成斐波那契二进制=齐肯多夫拆分的形式,那么发生的事情就像K=1一样了;我们只需要取出其所有齐肯多夫拆分中的最小最小项即可。而下面我们将说明实际上,任意整数的齐肯多夫拆分都是唯一的。

③对于i,我们试图将其用斐波那契数列中的数分解,并依据不能选连续项的原则,那么显然,若i只能用fj分解,则依据上述数学归纳法/DP的方法,易证齐肯多夫分解唯一性。
若i不选fj,则剩下的fibonacci数能搞出的最大的和显然是从fj1开始往前跳着一直选到一个1(它不分奇偶都会达到一个1的),但是这个最大和是多少呢?显然,这虽然有一坨项,但只需要为它加上一个1,它就会啪啪啪地全合成一项——fj,也就是,如果不选fj的话,我们最大只能搞出fj1,亦即,fj是我们的唯一选择,即
齐肯多夫分解唯一。
④(这个命题对于斐波那契数列来说有多种证明方式,但是这里我们介绍一种易于向高维推广的)
我们考虑A与B博弈,A必胜的时候,在某一轮中,B面对y,y的齐肯多夫拆分最小项为fi,这次B取了x,yx的齐肯多夫拆分最小项为fj
而若fj>2x,就意味着fj大于2倍的x的齐肯多夫拆分的最大项,即x的齐肯多夫拆分可与yx的齐肯多夫拆分简单合并而得到y,也就是说存在y的齐肯多夫拆分其最小项<fi,然而齐肯多夫拆分是唯一的,所以这种情况是不可能的。即fj2x必然成立,A每次一定能取到最小项。


然后我们开始试图向高维推广齐肯多夫定理及其推论。
考虑齐肯多夫定理的证明过程,显然我们只需要把fi=fi1+fi2换成fi=fi1+fk|Kfk1<fi1Kfk,就让齐肯多夫定理中表述的条件若干不连续若干项,其中任意两者之比大于K取代了;即原先fi是两个fi1和它的上一项合并而成,而现在它是由fi1和最小的不小于fi1K代替了。
而我们很容易就可以发现,上述四点的证明甚至几乎不用有太大的变化,便可完全套用在新的数列上;而且足够严谨。


最后我们分析一下时间复杂度的问题,这几乎已经是显然的了。
显然从前一项到后一项,至少要乘K+1K,而我们需要搞出小于等于N的所有项才行。
所以最后的时间复杂度是O(TlogK+1KN)2108,常数被卡飞了,极限数据的话怎么调都是1.0?秒。。而且由于代码异常简单,根本就没有常数优化的空间,导致非常蛋疼。。
不过。。根据出题人懒惰定律,出题人有相当大地可能性会出随机数据。而如果是随机数据的话,可以期望去掉一个2,0.5s差不多是可以的。。
而事实,也确实是这样。。


还有一种推数列的方法是搞两个数组,实际上与这种递推是可以互相推出的,写起来还要麻烦一点;这里就不再赘述了。


Code:

#include<cstdio>int A[1000000];inline int in(){    char c=getchar();    int x=0;    while(c<'0'||c>'9')c=getchar();    for(;c>='0'&&c<='9';c=getchar())x=x*10+c-'0';    return x;}int main(){    int i,N,K,tot,p;    A[1]=1;    for(int T=in(),t=1;t<=T;++t){        N=in(),K=in();        for(tot=2,p=1;A[tot]<=N;){            while((long long)A[p]*K<A[tot])++p;            A[tot]=A[tot++]+A[p];        }        printf("Case %d: ",t);        if(A[--tot]==N)puts("lose");        else{            while(N!=A[tot]){                N-=A[tot];                while(N<A[tot])--tot;            }            printf("%d\n",N);        }    }}

这道题给我的收获有:
①对于奇怪的DP、博弈论什么的,打表寻找规律,再试图证明规律,便很容易向高维推广了。
②DP在证明中的应用(DP->数学归纳法):证明第一项成立;证明若前n项成立,则下一项也成立。
③复杂的问题往往看起来难以下手,但实际上如果我们先考虑一些简单的情况,即使是再复杂的问题便也会迎刃而解了。

0 0