【hdu 2486】A simple stone game
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 592 Accepted Submission(s): 341
Problem Description
After he has learned how to play Nim game, Mike begins to try another stone game which seems much easier.
The game goes like this: Two players start the game with a pile of n stones. They take stones from the pile in turn and every time they take at least one stone. The one who goes first can take at most n-1 stones for his first move. From then on a player can take at most k times as many stones as his opponent has taken last time. For example, if one player take m stones in his turn, then the other player can take at most k × m stones next time. The player who takes the last stone wins the game. Suppose that those two players always take the best moves and never make mistakes, your job is to find out who will definitely win the game.
Input
The first line contains a integer t, indicating that there are t test cases following.(t<=20).
Each test case is a line consisting of two integer n and k.(2<=n<=10^8,1<=k<=10^5).
Output
For each test case, output one line starting with “Case N: ”, N is the case number. And then, if the first player can ensure a winning, print the minimum number of stones he should take in his first turn. Otherwise, print “lose”. Please note that there is a blank following the colon.
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
【题目链接】:http://acm.hdu.edu.cn/showproblem.php?pid=2486
【题解】
/*
首先分类讨论一下;
①k=1
则除了2^i之外,都是先手赢;
先手可以进行如下的策略;
把剩余的石子的数目转换成二进制;
先手每次可以把二进制表示的最后一个1给删掉;
比如6的二进制110
则减去2
->100
这时对方是没办法减去这一个1的,因为对方不能取的比你大(k=1)
所以对方只能取1或2
如果取1的话
->011
你还是按照一样的策略每次取最后一个1;
对方还是不能取倒数第二个1;
....
"所以你总是能取最后一个1。"
也即你能赢.
取2同理
总之你只要每次都取最后一个1表示的数,则你总是可以赢;
当然如果是2^i,则你不能取走仅有的一个1;
只能减去1..2^i-1中的任意一个数字;
如果你取到只剩下2^(i-1)以下,则对方直接赢了(对方最多可以取2^(i-1)个);
但是如果你取到剩下2^(i-1)以上;
那二进制形式里面必然还剩下2个以上的1;
则对方可以用上述策略击败你;(每次取最后一个1);
②k=2
则除非n为斐波那契数列的某一项,否则先手一定赢;
(1,2,3,5,8,13...)
有性质:任意一个整数x总能分解成若干项不相邻的斐波那契数列的和;
(这里的整数x是不属于斐波那契数列中的任意一项的整数);
且有f[i+2]>2*f[i];
那么如果一个整数不是斐波那契数列
比如12=1+3+8
则可以把它类比成二进制的形式111
我们还是每次取走最后一个1;
这里的1代表的是1;
就变成110
但是对方是没办法取走倒数第二个1的
因为f[i+2]>2*f[i];(任意一个整数可以由不相邻的斐波那契数相加构成)
这就说明在对方可取的范围里面不足以减去第二个1;
对方只能把倒数第二个1拆掉;
这样可以保证总是你取走最后一个1;
所以你总是能够赢.
相应的如果你面临了斐波那契数列
则你必须把那唯一的一个1拆掉(类似二进制);
则对方可以任意地拆掉你留给它的最后的一个1;
然后让你面临尴尬的局面。balabala
③k>2
我们考虑构造像k=2的那总数列a;
设我们已经构造到数列a第的i项a[i]了,且1..b[i](内除了这个数列的元素)
都能由若干个数列ai的元素构成且它们相邻之间a[xn]/a[xn-1]>k
那么有a[i+1]=b[i]+1;
因为b[i]+1这个元素没办法由1..a[i]构造成;
所以只能新加这么一项了;
然后要求b[i+1]了;
显然b[i]+1..b[i+1]内这些数的构成都要a[i+1]参与;
现在的问题是要给a[i+1]寻找"合作的伙伴";
显然那些合作伙伴的下标t;
要满足k<a[i+1]/a[t];
可以维护这么一个t值(因为a[i]是单调递增的,所以很好维护的);
则b[i+1]=a[i+1]+b[t];
(1..b[t]都是能用不超过a[t]的项构成的(它们之间的倍数关系大于k))
如果不存在t的话b[i+1]=a[i+1]=b[i]+1;
最后看看n在不在a数列中,不在的话先手赢,否则先后输;
具体原因上面已经讨论过了(都是类比二进制);
(k=1、2的情况都能归结到这种情况);
是先手赢的话一直减a数列中的数(从大到小能减就减);
最小的那个数字肯定就是我们上面一直讨论的"数字1"了;
你把它输出就好;
最坏情况是n=1e8,k=1e5
看看数列的长度是多少就可以了;(作为数列的size);
*/
【完整代码】
#include <bits/stdc++.h>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define LL long long
#define rep1(i,a,b) for (int i = a;i <= b;i++)
#define rep2(i,a,b) for (int i = a;i >= b;i--)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define rei(x) scanf("%d",&x)
#define rel(x) scanf("%I64d",&x)
typedef pair<int,int> pii;
typedef pair<LL,LL> pll;
const int MAXN = 1000000;
const int dx[9] = {0,1,-1,0,0,-1,-1,1,1};
const int dy[9] = {0,0,0,-1,1,-1,1,-1,1};
const double pi = acos(-1.0);
int T;
LL n,k;
LL a[MAXN],b[MAXN];
int main()
{
//freopen("F:\\rush.txt","r",stdin);
int ii = 0;
for (rei(T);T--;)
{
ii++;
rel(n);rel(k);
a[1] = b[1] = 1;
LL i = 1,t = 0;
while (a[i]<n)
{
i++;
a[i] = b[i-1]+1;
while (a[t+1]*k < a[i]) t++;
if (a[t]*k<a[i])
b[i] = a[i] + b[t];
else
b[i] = a[i];
}
printf("Case %d: ",ii);
if (a[i]==n)
puts("lose");
else
{
int ans = -1;
rep2(j,i-1,1)
if (n>=a[j])
{
n-=a[j];
ans = a[j];
}
printf("%d\n",ans);
}
}
return 0;
}