CF1929C Sasha and the Casino 题解
题目大意
萨沙去赌场,赌场是按照以下规则运作的。假设下注
y
y
y 硬币(
y
y
y 是正整数),那么如果获胜,将获得
y
⋅
k
y\cdot k
y⋅k 枚
硬币(即,他的硬币数量将增加
y
⋅
(
k
−
1
)
y\cdot(k-1)
y⋅(k−1) 枚)。如果输了,他将输掉全部赌注(即,他的硬币数量将减少
y
y
y 枚)。
同时萨沙知道了赌场有一个促销活动:每个客户最多输 x x x 次。
最初,萨沙有 a a a 枚硬币。他想知道自己是否可以按照某种方法下注,从而保证一定可以赢得无限数量的硬币。
2 ≤ k ≤ 30 , 1 ≤ x ≤ 100 , 1 ≤ a ≤ 1 0 9 2\leq k\leq 30,1\leq x\leq 100,1\leq a\leq 10^9 2≤k≤30,1≤x≤100,1≤a≤109
解题方法
考虑怎样的策略是最优的,可以在每一次赢的时候都可以连本带利地把钱赚回来。
因为最多输 x x x 次,所以可以把每 ( x + 1 ) (x+1) (x+1) 轮看作一个轮回,同时只要考虑前 x x x 轮全输的最坏情况即可。(因为如果我们在最坏情况下都可以回本的话,那不输到 x x x 轮就赢了我们肯定也能回本,回的还更多)。
乍一看没有思路,尝试模拟样例:
2 1 7
输出是 YES
。
第一次投 1 1 1 个,输,还剩6个;
第二次投 1 1 1 个,必须赢,还剩 5 + 1 × 2 = 7 5+1 \times 2=7 5+1×2=7 个;
循环往复,我们可以赚到无限多的钱。
3 3 6
输出是 NO
。
第一次投 1 1 1 个,输,还剩 5 5 5 个;
第二次投 1 1 1 个,输,还剩 4 4 4 个;
第三次投 2 2 2 个,输,还剩 2 2 2 个(本轮投 1 1 1 个,赢的话还剩 3 + 1 × 3 = 6 3+1 \times 3=6 3+1×3=6 个,只能回本,赚不了钱);
第四次只能投 2 2 2 个(此时只剩 2 2 2 个金币),必须赢,还剩 0 + 2 × 3 = 6 0+2 \times 3=6 0+2×3=6 个;
发现我们每次 ( x + 1 ) (x+1) (x+1) 轮的轮回都卡在 6 6 6 个金币上,会陷入死循环,所以不行。
通过模拟样例,我们好像发现了一个规律:开始投 1 1 1;后来每一轮投的数量是 y y y,保证如果本轮赢的话, y × k > y \times k> y×k> 前面的总投注。
设当前进行到第 t t t 轮, b i b_i bi 表示第 i i i 轮赢的话可以赚钱的最少下注钱数。
则可以推出数学公式:
b t × k > ∑ i = 1 t b i b_t \times k > \sum\limits_{i=1}\limits^{t}b_i bt×k>i=1∑tbi
等式两边各消掉一个 b t b_t bt,得
b t × ( k − 1 ) > ∑ i = 1 t − 1 b i b_t \times (k-1) > \sum\limits_{i=1}\limits^{t-1}b_i bt×(k−1)>i=1∑t−1bi
即
b t > ∑ i = 1 t − 1 b i k − 1 b_t > \dfrac{\sum\limits_{i=1}\limits^{t-1}b_i}{k-1} bt>k−1i=1∑t−1bi
因为我们要赚钱,也就是说每一轮的投注金额都要在满足如果当前轮赢可以赚钱的条件下尽量取最低。
变换公式为
b t ≥ ⌊ ∑ i = 1 t − 1 b i k − 1 ⌋ + 1 b_t \ge \lfloor \dfrac{\sum\limits_{i=1}\limits^{t-1}b_i}{k-1}\rfloor + 1 bt≥⌊k−1i=1∑t−1bi⌋+1
所以每一轮下注的就是最小的满足条件的金额,即
b t ≥ ⌊ ∑ i = 1 t − 1 b i k − 1 ⌋ + 1 b_t \ge \lfloor \dfrac{\sum\limits_{i=1}\limits^{t-1}b_i}{k-1}\rfloor + 1 bt≥⌊k−1i=1∑t−1bi⌋+1
这样判断条件就是:只循环一个轮回(
x
+
1
x+1
x+1 轮),如果在每一轮的投注金额是满足如果当前轮赢可以赚钱的条件的最低值,且到第
(
x
+
1
)
(x+1)
(x+1) 轮时还有钱,说明一定可以赚到无限多的钱,输出 YES
;
如果半道没钱了,那么连回本都是问题,更别说赚无限的钱了,输出 NO
。
这样每次只要循环 ( x + 1 ) (x+1) (x+1) 次,时间复杂度约为 O ( T x ) O(Tx) O(Tx)。
AC 代码
#include <iostream>
#include <vector>
using namespace std;
typedef long long ll;
void solve()
{
int k, x, a;
cin >> k >> x >> a;
ll sum = 0; // 记录前面轮投注的总钱数,要开long long
vector <int> b; // 记录每一轮的投注
b.push_back(1); // 第一轮固定投1个
for(int i = 0; i < x + 1; i ++){ // 循环x+1轮
sum += b[i];
b.push_back(sum / (k - 1) + 1); // 计算下一轮的最小投注
if(sum > a){ // 没有钱了
cout << "NO\n";
return ;
}
}
cout << "YES\n"; // 没钱的情况在循环末尾已经判断完了,这里就不用判断了
}
int main()
{
ios :: sync_with_stdio(false); cin.tie(0);
int T; cin >> T;
while(T --) solve();
return 0;
}
End
谢谢大家!