在一个游戏中你须要作 \(k\) 个火把,其中每一个火把由 \(1\) 个木棍和 \(1\) 个煤炭组成html
游戏开始时你拥有 \(1\) 个木棍,能够执行任意次如下两种操做之一python
- 用 \(1\) 个木棍换 \(x\) 个木棍
- 用 \(y\) 个木棍换 \(1\) 个煤炭
请给出制造 \(k\) 个火把最小须要的操做数ios
\(2 \leq x \leq 10^9\)c++
\(1 \leq y, k \leq 10^9\)算法
math
*1000
api
这题样例错了,WA
死我了!!!数据结构
这题的坑点在于 ceil
函数的精度不足以覆盖这个题目的数据范围函数
首先,咱们总共须要 k * y + k
个木棍,咱们每次进行操做一能够得到 x - 1
个木棍。设操做一的操做数为 cnt
学习
,则存在关系:优化
故答案为:
注意 ceil
应为: ceil(x, y) = (x + y - 1) / y
void solve(){ LL x, y, k; cin >> x >> y >> k; LL need = k * (y + 1) - 1; cout << (need / (x - 1)) + k + !(need % (x - 1) == 0) << endl; } int main(){ int t = read(); while (t--) solve(); return 0; }
有以下注意点:
api
。给定一个有 \(n\) 个元素的序列 \(arr\) ,并给定序列 \(arr\) 中各元素是否被锁定(可否移动,修改)。
请你输出 \(k\) 值最小时对应的序列。
其中, \(k\) 值为:\(\max j \rightarrow where \{ p_j <0 \},p_j = \sum_{i=1}^{j}a_i\),换一句话说,使得序列前缀和小于 \(0\) 的最大索引。若不存在这样的索引则 \(k = 0\) 。
- \(1 \leq n \leq 100\)
- \(-10^5 \leq arr_i \leq 10^5\)
greedy
sortings
*1300
很简单,他不须要你输出 \(k\) 值,只须要你输出对应的序列。贪心的去想,显然咱们但愿全部未被锁定的元素能从大到小排。
const int maxn = 1e2 + 50; int f[maxn], ilock[maxn], can[maxn]; // ilock -> 是否锁定, can -> 未被锁定的序列 void solve(){ int n = read(), cnt = 0; for (int i = 0; i < n; ++ i) f[i] = read(); for (int i = 0; i < n; ++ i) { ilock[i] = read(); if (ilock[i] == 0) can[cnt++] = f[i]; } sort(can, can + cnt, greater<int>()); int pt = 0; for (int i = 0; i < n; ++ i) if (!ilock[i]) f[i] = can[pt++]; print(f, n); // 自定义输出函数 } int main(){ int t = read(); while (t--) solve(); return 0; }
有以下注意点:
你和你的朋友须要闯关打 boss ,大家有必定数量的无敌火箭炮!,能够直接秒杀任何 boss。已知有两种 boss,简单 boss 和 困难 boss。
大家须要协助经过 \(n\) 关(这意味着大家须要共杀死 \(n\) 个 boss)。你和你的朋友均可以选择击杀 \(1\) 或者 \(2\) 个 boss。须要注意的时:
- 你能够击杀全部 boss
- 你的朋友只能击杀简单 boss
- 你的朋友先进行闯关
请输出闯关所需的使用无敌火箭炮的最小数量。
- \(1 \leq n \leq 2\cdot 10^5\)
dp
greedy
1500
好像有贪心算法的题解,可是显然这题是个裸的 dp
。定义 dp
:dp[i][j] := 第 i 关由 j 完成的最小开销
,其中 j
只有 0 or 1 表明你和你的朋友。
const int inf = 0x3f3f3f3f; const int maxn = 2e5 + 50; int dp[maxn][2], a[maxn]; void solve(){ int n = read(); for (int i = 1; i <= n; ++ i) a[i] = read(); dp[0][0] = dp[0][1] = 0; dp[1][0] = a[1], dp[1][1] = inf; dp[2][0] = a[1] + a[2]; dp[2][1] = dp[1][0]; for (int i = 3; i <= n; ++ i){ dp[i][0] = min(dp[i - 1][1] + a[i], dp[i - 2][1] + a[i] + a[i - 1]); dp[i][1] = min(dp[i - 1][0], dp[i - 2][0]); } cout << min(dp[n][0], dp[n][1]) << endl; } int main(){ int t = read(); while (t--) solve(); return 0; }
有如下注意点:
dp
思路混乱,代码过于暴力,不够优美Vova 决定打扫房间。能够将房间表示为坐标轴 OX 。房间里有 \(n\) 堆垃圾,第 \(i\) 堆的坐标是整数 \(p_i\) 。全部桩具备不一样的坐标。
让咱们将总清理定义为如下过程。此过程的目标是将全部桩收集至不超过两个且互不相同的 \(x\) 坐标下。为了实现此目标,Vova 能够执行几回(可能为零)移动。在移动期间,他能够选择一些 \(x\),并使用扫帚将全部堆(没法控制数量)从 \(x\) 移动到 \(x + 1\)或 \(x-1\)。
此外会有 \(q\) 个询问,共两种查询类型:
0 x
- 从坐标 \(x\) 移除一堆垃圾。确保此时在坐标 \(x\) 中有一个桩。1 x
- 将一堆垃圾添加到坐标 \(x\) 。能够确保此时在坐标 \(x\) 中没有桩。请注意,有时房间中的垃圾堆为零。
Vova 想知道,在全部询问前完成总清理和每次询问后完成总清理的最小移动次数,请注意,总清理实际上并无发生,也不会改变堆的状态。它仅用于计算移动次数。
- \(1 \leq n, q \leq 10^5\)
- \(1 \leq p_i \leq 10^9\)
data structures
hashing
*2100
题目贼长 ,实际上意思还算比较简单,须要咱们按照规律进行垃圾清理,保证操做次数最小且符合垃圾清理的要求。给定了 \(q\) 个询问,且 \(q\) 的数量级还不低,因此确定须要咱们使用较为高效的数据结构来解决问题。
题目须要咱们将全部堆移动到最多两个位置,不妨定义 \(x_i, x_j\) 分别为最后移动的两个位置且 \(x_i < x_j\) 。
上述序列,\(x_{\min}, x_{\max}\) 表明最左最右的堆,定义 \(x_k\) 归于 \(x_i\) 侧,\(x_{k + 1}\) 归于 \(x_{j}\) 侧。因此最终答案为:
通过分析发现,实际上答案能够定义为 最大x - 最小x - 最大间隔gap
,因为\(x_{\max} - x_{\min}\) 是固定的,gap
越大答案越小。
所以:本题须要解决,高效获取序列中最大间隔值(差值),和最大最小值。
固然,本身写数据结构是不太明智的,咱们能够经过已有的数据结构例如: set multiset
或 map
进行解决。
代码借鉴了红名大佬的代码(手上的电脑只能写 py , 特别麻烦遂做罢)
#include <bits/stdc++.h> int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); int n, q; std::cin >> n >> q; std::set<int> p; // pos arr std::multiset<int> d; // dist arr // 添加节点 auto add = [&](int x) { auto it = p.insert(x).first; auto r = std::next(it); if (it != p.begin() && r != p.end()) d.erase(d.find(*r - *std::prev(it))); if (it != p.begin()) d.insert(x - *std::prev(it)); if (r != p.end()) d.insert(*r - x); }; // 删除节点 auto del = [&](int x) { auto it = p.erase(p.find(x)); if (it != p.begin()) d.erase(d.find(x - *std::prev(it))); if (it != p.end()) d.erase(d.find(*it - x)); if (it != p.begin() && it != p.end()) d.insert(*it - *std::prev(it)); }; for (int i = 0; i < n; ++i) { int x; std::cin >> x; add(x); } // 返回询问 auto get = [&]() { if (p.size() <= 1) return 0; return *p.rbegin() - *p.begin() - *d.rbegin(); }; std::cout << get() << "\n"; while (q--) { int t, x; std::cin >> t >> x; if (t == 0) { del(x); } else { add(x); } std::cout << get() << "\n"; } return 0; }
有以下注意点:
set
的用法,很致命,须要熟悉 set
prev(iter) = arr.rbegin()
的用法,其中 rbegin()
是反转迭代器!!!计算指望 \(E\),你拥有一个耐力值为 \(a\) ,防护值为 \(b\) 的盾牌,你将遇到 \(n\) 个怪兽,每一个怪兽的力量值为 \(d_i\)。
知足以下规律:
- 若是 \(a == 0\),你会收到 \(d\) 的伤害。
- 若是 \(a >0 \land d \geq b\),你不会收到伤害,可是盾牌的耐力值减一:\(a \leftarrow a-1\)。
- 若是 \(a > 0\land d < b\),你不会收到伤害。
总共须要处理 \(m\) 个盾牌的状况。请计算每一个盾牌给定 \(a_i,b_i\) 下的收到伤害的指望。
其中,预期必定是一个不可约分数 \(\frac{x}{y}\),\(y\) 于 \(998244353\) 互素。你须要输出 \(x\cdot y^{-1} mod \;998244353\) 的值。其中\(y^{-1}\)为\(y\)的逆元。
- \(1 \leq n,m \leq 2\cdot 10^5\)
- \(1 \leq a_i \leq n\)
- \(1 \leq b_i, d_i \leq 10^9\)
probabilities
combinatorics
binary search
*2400
首先,怪兽分为大怪兽和小怪兽两种,设大怪兽数量为 \(k\)。当 \(a \ge k\) 时答案为 \(0\)。
对于大怪兽,能形成伤害的几率为 \(P_1= 1 - \frac{a}{k}\) (最多抵消 \(a\) 个)。
对于小怪兽,咱们能够将 \(k\) 个大怪兽固定好,所以具备 \(k + 1\) 个间隔,前 \(a\) 个间隔都没法形成伤害,所以能形成伤害的几率为:\(P_2= 1 - \frac{a}{k + 1}\)。
所以,答案为:
对于求和,能够用二分查找 + 前缀和优化
而后按照费马小定律求逆元便可。
import os,io input=io.BytesIO(os.read(0,os.fstat(0).st_size)).readline # 快读 import bisect mod = 998244353 def fpow(a, b): ans = 1 while b: if (b & 1): ans = (ans * a) % mod; b >>= 1; a = (a * a) % mod; return ans n, m = map(int, input().split()) d = list(map(int, input().split())) d = sorted(d) pre = [0 for _ in range(n + 1)] for i in range(n): pre[i + 1] = pre[i] + d[i] for _ in range(m): a, b = map(int, input().split()) k = bisect.bisect_left(d, b) # 二分查找 low = max(0, n - k - a) upp = max(0, n - k - a + 1) # 1 - (a / k) print((low * fpow(n - k, mod - 2) * (pre[n] - pre[k] + mod) % mod + upp * fpow(n - k + 1, mod - 2) * pre[k] % mod) % mod)