codeforces #39E

题目大意:有两个人玩游戏,每次对a或b进行加1的操作,使得a^b<n,若不可操作则为负,先判断是否平局,若有必胜手段,输出负者

解题思路:博弈类型动态规划,这里采用的是记忆化搜索,没有接触过此类问题的可以先做一下UVAOJ的10404,题目链接:

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1345

在此题中,为了叙述方便,将stas设为第一人称我,则stas赢即为“我赢”,反之为“我负”

接下来说一下这道题目中的几个坑点:

首先是判断是否为平局,a=1就一定为平局?no,只有当a=1且如果我让a=a+1时,我没有胜利的策略时才让b=1,对手亦然,直到不能让a+1(即(a+1)^b>=n,或者b>40,因为2^40差不多也会超过10^9,这里很容易漏)时才是平局。

接着就是考虑a^b的计算,显然要用快速幂(直接取对数只有在初始的时候可以用来计算,否则会因为精度问题卡到死),又初始的时候a,b显然可能直接超过n,所以需要取对数特判。

接着就是b=1的情况,这种情况按理应该不会有什么问题,但是仔细细想一下如果双方分别让a=a+1则会超时(因为让b=b+1)没有必胜策略啊!那这时就应该通过判断单调性来直接优化!


AC代码:

#include <iostream>
#include <string.h>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
int dp[100010][70];
LL a,b,n;
LL pow_mod(LL a,LL b)
{
	if(b == 0) return 1;
	LL x = pow_mod(a,b>>1);
	LL ans = x*x;
	if(b&1) ans = ans*a;
	return ans;
}
int dfs(LL a,LL b)
{
	if(pow_mod(a,b)>=n) return 0;
	if(dp[a][b] != -1) return dp[a][b];
	if(a == 1)
	{
		if(b>=40 || b*b>=n) return dp[a][b] = 2;
		if(dfs(a+1,b) == 1) return dp[a][b] = 0;
		if(dfs(a,b+1) == 0) return dp[a][b] = 1;
		if(dfs(a,b+1) == 2) return dp[a][b] = 2;
	}
	if(b == 1)
	{
		for(int i = 0;(i+a)*(i+a)<n;i++)
		{
			if(i % 2 == 0 && dfs(i+a,2) == 1)
			return dp[a][b] = 0;
			if(i % 2 == 1 && dfs(i+a,2) == 1)
			return dp[a][b] = 1;
		}
		if((n-a) % 2 == 1) return dp[a][b] = 1;
		else return dp[a][b] = 0;
	}
	if(dfs(a+1,b) == 1 || dfs(a,b+1) == 1) return dp[a][b] = 0;
	else return dp[a][b] = 1;
}
int main()
{
	memset(dp,-1,sizeof(dp));
	cin >> a >> b >> n;
	if(b*log(a)>=log(double(n))) cout << "Stas" << endl;
	else if (dfs(a,b) == 0) cout << "Masha" << endl;
	else if (dfs(a,b) == 2) cout << "Missing" << endl;
	else if (dfs(a,b) == 1) cout << "Stas" << endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值