暑假牛客多校第一场 2023-7-18(D、J)

未补完

D. Chocolate


code
#include "bits/stdc++.h"

signed main()
{
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);

    int n, m;
    std::cin >> n >> m;
    if (n == 1 && m == 1) {
        std::cout << "Walk Alone\n";
    } else std::cout << "Kelin\n";

    return 0;
}

 

J. Roulette


概要:一个人起初有n块钱,他想赢到n + m块钱。游戏规则是一开始他得下注1块钱,在第i - 1场下注中若赢了则得到下注的双倍奖励同时在第i场中下注必须为1,若输了,则下一次下注的前为上一场下注的前的两倍(像赌徒),但是每次下注前都必须先付钱才能下注。问:得到n + m块钱的概率。

算法:快速幂、快速幂求逆元、费马小定理

做法:仔细研究题目,可得在每一次下注时我们在满足目前下注所需金额的前提下,我们可以一直是输,最后一把赢回来。例如输1、2、4、8,接下来赢16,结果总是钱的数目加1
若我们现在有x块钱,则我们可以连续输的最大场数为 \(log(x + 1)\) 。设 \(k = log(x + 1)\) ,则成功获得一块钱的概率为 \(1 - \frac{1}{2^k}\)。另外,某一些区间的最大可连续输的场次是相同的,又因为每输一次就加倍赌注,那么我们可以把[n, n + m - 1]优化为 \(log\) 级别的时间复杂度,每一次概率的叠加也要用到快速幂,毕竟一段区间中很多数可连续输的场次都相同。
求概率时我们对于 \(\frac{1}{2^k}\) 求其逆元。

code
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#include <queue>
#include <stack>
#include <deque>
#include <cmath>
#include <string>
#include <set>
using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, int> PDI;
typedef pair<double, double> PDD;
int dx[4] = { -1, 1, 0, 0 }, dy[4] = { 0, 0, -1, 1 };

const int p = 998244353;

int n, m;

int power(int x, int k)
{
	int res = 1, a = x;
	while (k)
	{
		if (k & 1)res = (LL)res * a % p;
		a = (LL)a * a % p;
		k = k >> 1;
	}
	return res;
}

int inv(int x)
{
	return power(x, p - 2);
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	cin >> n >> m;

	int inv_2 = inv(2);
	int ans = 1;
	for (int i = n; i < n + m; )
	{
		int lg = log2(i + 1);
		int pro = (1 - power(inv_2, lg) + p) % p;
		int j = min((LL)(m + n) - 1, ((LL)1 << (lg + 1)) - 2);
		ans = (LL)ans * (LL)power(pro, j - i + 1) % p;
		i = j + 1;
	}

	cout << ans;
	return 0;
}
posted @ 2023-07-22 16:42  dkdklcx  阅读(93)  评论(0)    收藏  举报