小白月赛 50 题解
A. 跳跃
从第二个数字开始,判断每一个数字是否至少是前一个数字的 倍,或者前一个数字是否至少是当前数字的 倍。要注意问题的转换,如果使用 int 读入,则尽量不要使用除法(因为除法默认整除,会向下取整,使得本题细节更多)
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 5; int a[maxn]; int main(){ int n, sum = 0, k; cin >> n >> k; for(int i = 1; i <= n; i++){ cin >> a[i]; } for(int i = 2; i <= n; i++){ if(a[i] > a[i-1] * k){ sum++; }else if(a[i] * k < a[i-1]){ sum++; } } cout << sum << endl; }
B. 减法和除法
由于减法和除法都是将数字变小,而我们的目标就是将数字变得尽可能地小,所以我们可以去判断到底哪一种操作会使得数字变得更小,来使用对应的运算符。
#include <iostream> using namespace std; int main(){ int n, x, ans = 0; cin >> n >> x; while(n > 0){ n = min(n / 2, n - x); ans++; } cout << ans << endl; }
C. 减法和求余
本题有三种情况
- 所有数字均为 0,无需进行任何操作,操作次数为 0。
- 所有数字的最大公约数大于等于 2,这时可以令 为所有数字的最大公约数,然后进行求余操作,使得所有数字全部变为 0,操作次数为 1。或者所有数字均为 1,此时可以通过一次操作 2 来将所有数字变为 0。
- 将所有数字全部对 2 进行求余,此时所有数字要么是 0,要么是 1,将所有 1 进行减一操作变为 0,操作次数为 2。
#include <bits/stdc++.h> using namespace std; int main(){ int n, a, cnt01 = 0, gcd = 0, cnt0 = 0; cin >> n; for(int i = 1; i <= n; i++){ cin >> a; gcd = __gcd(gcd, a); if(a == 1){ cnt01++; } if(a == 0){ cnt01++; cnt0++; } } if(cnt0 == n){ cout << 0 << endl; }else if(cnt01 == n || gcd > 1){ cout << 1 << endl; }else{ cout << 2 << endl; } }
D. 生日
考虑在第 天过生日的人产生的快乐值对答案的贡献。在第 天过生日的人只可能有三个人,即 、、,我们只需要讨论这三个人的过生日选择(即他们选择是否在第 天过生日),就可以知道第 天能够产生多少快乐值了。将这个值乘以 就可以得到第 天的贡献了。因为每个人有两种选择,已经确定了三个人,那么剩余人随便选择的方案数就是
要注意讨论 和 不存在的情况。
本题也可以不使用快速幂,而是预处理好 次方对 取模后的结果。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 5; #define ll long long ll mod = 1e9 + 7; int a[maxn], mi[maxn]; int main(){ ll n, ans = 0; cin >> n; mi[0] = 1; for(int i = 1; i <= n; i++){ cin >> a[i]; mi[i] = mi[i-1] * 2 % mod; } for(int i = 1; i <= n; i++){ ll now = 0; if(i * 2 <= n){ now += a[i] ^ a[i*2]; } if(i * 2 + 1 <= n){ now += a[i] ^ a[i*2+1]; now += a[i] ^ a[i*2+1] ^ a[i*2]; now += a[i*2] ^ a[i*2+1]; ans += now * mi[n-3]; }else{ ans += now * mi[n-2]; } ans %= mod; } cout << ans << endl; }
E. 工艺品
本题有三种情况
- 自己制作工艺品的速度比加工半成品的速度快。此时所有工艺品全部自己做。
- 自己制作工艺品的速度比加工半成品的速度慢,此时应当优先满足加工半成品的需求。我们可以算出最多能够加工多少半成品,然后剩余时间全部自己制作工艺品。最多能够加工半成品的数量由制作速度较慢的人来决定(即 和 的较大值),我们可以再分两种情况讨论 的较大值来算出答案。
#include <iostream> using namespace std; int main(){ int n, a, b, c; cin >> n >> a >> b >> c; if(a < c){ cout << n / a; }else{ int now = min((n - c) / b, (n - b) / c); // 表示制作 now 件半成品,数量由较慢的人来决定 cout << now + (n - now * c) / a; // 制作 now 件半成品之后,剩余时间自己加工 } }
F. 数位和
先考虑不带修改的情况:
对于某一个数字而言,假设它的个位为 ,此时我们去求除了个位之外的数字的数位和,可以获得一个值 ,我们将 得到 ,可以发现,这个 一定是一个一位数。如果我们想要让这个数字的数位和被 10 整除,那么个位 需要满足的条件就是 ,由于 是个位数,所以我们可以解得唯一的一个 。也就是说,从零开始,每十个数字有且仅有一个数字的数位和是 10 的倍数。
所以答案一定与 有关。
我们发现 27 和 28 对应的答案分别是 1 和 2(即 有一个数字的数位和是十的倍数,而 有两个),但是 和 的结果都是 2。(此处的除法是整除),也就是说有的时候我们除以十可以获得正确答案,有的时候会多算一个,需要减掉。判断是否需要减掉的方法和上面的证明过程类似,即获得除了个位之外的数位和,然后解得唯一的 ,看看给定的数字的个位和 的关系,如果大于等于 ,则不会多算,否则说明多算了一个,需要减掉。
带修改之后的做法相似,只需要预处理出 的结果,就可以算出单点修改对答案的影响了。
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 5; long long po[maxn]; int main(){ string s; int q; cin >> s >> q; long long sum = 0, now = 0, mod = 1e9 + 7, len = s.size(); for(int i = 0; i < len - 1; i++){ int nn = s[i] - '0'; sum += nn; now = (now * 10 + nn) % mod; } po[0] = 1; for(int i = 1; i < len; i++){ po[i] = po[i-1] * 10 % mod; } int need = (10 - sum % 10) % 10; bool flag = false; if(s[len-1] - '0' < need){ flag = true; } cout << (now - flag + mod) % mod << endl; int a, b; while(q--){ cin >> a >> b; int wei = len - a, last = s[a-1] - '0'; wei--; if(wei != -1){ now -= last * po[wei] % mod - b * po[wei] % mod; now = (now % mod + mod) % mod; sum = sum - last + b; } need = (10 - sum % 10) % 10; a--; s[a] = '0' + b; if(s[len-1] - '0' < need){ flag = true; }else{ flag = false; } cout << (now - flag + mod) % mod << endl; } }