Codeforces Round #667 (Div. 3) B、C、D、E 题解
B. Minimum Product #枚举 #贪心
题意
给定四个整数a,b,x,y,其中a≥x,b≥y,你可以执行不超过n次的操作:选择a或者b,减一。操作保证a不会低于x,b不会低于y。现要你求出a与b的最小乘积。
分析
容易知道结论,要使乘积变小,一定是要将尽可能多的减少量放到某一个数,而不是将减少量均摊给两个数。[*不完全证明见后]
由此,最终结果无非是两种情况,1) 要么先对a做减法,只有当新a不能再做减法时,再对b做减法。2) 要么先对b做减法,只有当新b不能再做减法时,再对a做减法。于是我们对这两种情况得到的乘积进行比较,就能得到最小乘积了。
[不完全证明]:翻译自@pritishn思路,假设两个数字a,b,你可以将他们进行总共两次减法操作。无疑有三种选择:(i)a−1,b−1 ,得到的乘积为ab−a−b+1;(ii)a−2,b,得到乘积为ab−2b;(iii)a,b−2,得到乘积为ab−2a。
不妨设b>a,显然ab−a−b+1>ab−a−b>(因为b>a)ab−2b,显然情况(i)的乘积比情况(ii)大,同理也可以证明情况(ii)乘积比情况(iii)大。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #include <algorithm> #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <queue> #include <stack> #include <string> #include <vector> #include <cmath> using namespace std; typedef long long ll; const int MAXN = 2e5 + 5; const int MOD = 1e4 + 7; int q; int main(){ scanf("%d", &q); while(q--){ ll a, b, x, y, n; scanf("%lld%lld%lld%lld%lld", &a, &b, &x, &y, &n); ll cura = max(a - n, x); ll len = n - (a - cura); ll curb = max(b - len, y); ll ans1 = cura * curb;
curb = max(b - n, y); len = n - (b - curb); cura = max(a - len, x); ll ans2 = cura * curb; printf("%lld\n", min(ans1, ans2)); } return 0; }
|
C. Yet Another Array Restoration #数学 #暴力 #构造
题意
你需要构造一个数组,其中它有n个不同正整数,同时它必须包含x,y两个已知正整数(x<y),这个数组(a1<a2<...<an)经过升序排序后所有相邻元素必须差值相等,即a2−a1=a3−a2=...=an−an−1。现要你找到这样的数组并输出,同时该数组中的最大元素越小越好。
分析
题目要求构造的数组,显然是等差数列。因此我们核心思想就是确定首项及公差后,判断x,y是否在该数列即可。
怎么确定公差?我们知道公差一定是y−x的约数,dmax=y−x,我们就从d=1到d=dmax开始确定首项a0,为了保证数组最大值越小越好,一定是希望y越接近最大值越好(即y前面能够拓展足够多的元素)。于是我们从y开始递减,如果能够向前拓展到n个元素,说明当前的d是最优的。如果不行,我们就将a0确定在尽可能小的正整数,然后以a0,a0+d,a0+2d,...,a0+d∗(n−1)拓展元素,于是题目中要求的数组就确定下来了。公差的从小到大递增,一旦发现合法,同时又因为其枚举次序符合最优性,便可直接输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <algorithm> #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <queue> #include <stack> #include <string> #include <vector> #include <cmath> using namespace std; typedef long long ll; const int MAXN = 2e5 + 5; const int MOD = 1e4 + 7; int q; int main(){ scanf("%d", &q); while(q--){ int n, x, y; scanf("%d%d%d", &n, &x, &y); int diff = y - x; for(int d = 1; d <= diff; d++){ if(diff % d != 0) continue; if(diff / d + 1 > n) continue; int k = min((y - 1) / d, n - 1); int a0 = y - k * d; for (int i = 0; i < n; i++){ printf("%d%c", a0 + i * d, (i == n - 1) ? '\n' : ' '); } break; } } return 0; }
|
D. Decrease the Sum of Digits #模拟 #暴力
题意
给定正整数n,每次操作可对n(≤1018)自增。你需要求出最少操作次数(同时也就是“增数”),使得将n增至某个数后所有数位数字之和,不大于s。
分析
观察到n的长度最多为18,我们可以自数字低位到最高位,尝试对每一位进行归零(实际上就是将该位的数字加到进十),并实时检查当前递增后的n的数字之和是否满足题意,时间复杂度为O(log102(n)),对于该数据范围十分稳妥。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| #include <algorithm> #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <queue> #include <stack> #include <string> #include <vector> #include <cmath> using namespace std; typedef long long ll; const int MAXN = 2e5 + 5; const int MOD = 1e4 + 7; int q; int sum[20]; int Cal(ll x){ int res = 0; while(x){ res += (x % 10); x /= 10; } return res; } int main(){ scanf("%d", &q); while(q--){ ll n, tmp; int s, tot = 0; scanf("%lld%d", &n, &s); if(Cal(n) <= s) { printf("0\n"); continue; } ll mul = 1, ans = 0; for (int i = 0; i < 19; i++){ int cur = (n / mul) % 10; ll diff = (10 - cur) * mul; n += diff; ans += diff; if(Cal(n) <= s) break; mul *= 10; } printf("%lld\n", ans); } return 0; }
|
题意
(飞机和飞机场太不形象了)
有n个球,第i个球坐标为(xi,yi),他们正要掉下来,现在你只有两个长度为k的水平盘子(如果盘子左端点为x,则右端点为x+k,两端点也能装下球),需要你放置将这两个盘子放置到某个位置,将尽可能多的掉下来的球装下。求最多能装多少个球。
分析
感谢@issue敲腻害的思路。
题目中的yi对答案没有影响,我们只考虑xi,因而首先将所有球x坐标升序排序。接着,我们先考虑拿一个盘子去接球,从右到左枚举所有球的坐标,通过二分算法计算出该盘子当前从左端点起,能接到的球数量。为什么从右到左遍历?因为我们需要后缀和数组ri实时维护在区间[x[i],x[n]]中,任何位置放下盘子,能接到球的最大数量。
然后我们再拿另一盘子去接,此时从左到右枚举球的起点,并二分出能接到的数量。那么最后更新答案时,其实就是以i作为分界线, 第一个盘子装下左区间[x[i],x[i]+k] 的球数量,再加上第二个盘子在右区间(该右区间不一定连接于左区间)装下的球最大数量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <cstdio> #include <cstring> #include <iostream> #include <queue> #include <stack> #include <string> #include <algorithm> #include <vector> using namespace std; typedef long long ll; const int MAXN = 2e5+5; int q, n, k; int x[MAXN], y[MAXN], le[MAXN], ri[MAXN]; int main(){ scanf("%d", &q); while(q--){ scanf("%d%d", &n, &k); for(int i = 1; i <= n + 1; i++) ri[i] = 0; for(int i = 1; i <= n; i++) scanf("%d", &x[i]); for(int i = 1; i <= n; i++) scanf("%d", &y[i]); sort(x + 1, x + n + 1); for(int i = n; i >= 1; i--){ int xfar = x[i] + k; int pos = upper_bound(x + 1, x + 1 + n, xfar) - x; ri[i] = max(pos - i, ri[i + 1]); } int ans = -1; for(int i = 1; i <= n; i++){ int xfar = x[i] + k; int pos = upper_bound(x + 1, x + 1 + n, xfar) - x; ans = max(ans, pos - i + ri[pos]); } printf("%d\n", ans); } return 0; }
|