从这里开始
- 题目列表
- 小结
- Problem A Points on the line
- Problem B Our Tanya is Crying Out Loud
- Problem C Phone Numbers
- Problem D Alena And The Heater
- Problem E Cashback
- Problem F Machine Learning (Lazy Tag)
小结
这场比赛和同学一起打。本来应该是很开心的事情,结果留下好多遗憾。
第一个遗憾是没能在20分钟内消灭A、B题。然后分数就不是很可观。
我第一次交B题都20多分钟了。然后朋友们说我的贪心有锅,然后我就真信了。
于是我就重新写了个贪心交一发。好玩的是,考完我重新交了下B题之前的程序A了。
C、D题做得比较快,因为比较简单。(想吐槽一句。为什么cf div 2前4题只有普及组难度了?)
E题是最好玩的。读完题,写出了dp式子,我觉得会和按$c$分段有关,然后ZJC表示我在搞笑,我怎么又信了?
然后思路就被带偏了。然后写暴力dp打表找决策单调性,然而写挂了,样例都挂,然后就弃疗了。
比赛最后10分钟,红太阳QJX表示她A掉了E题,转移不是长度为1就是$c$。剩下的人全部在试图hack这个做法。(为什这个时候我就不信了qwq)
看到Doggu的一个朋友1小时A掉5道题,最后一题直接弃坑。我的内心是崩溃的。
主要还是A、B题做得太慢了吧。真正有意思的题目到后面都没时间思考。
综上所述,还是我太菜了。
Codeforces 940A Points on the line
题目大意
定义一个可重集的距离是它中间最大的两个数之间的差,特殊地,只有一个元素的可重集的距离为0。
给定一个可重集,问最少删掉多少个数使得它的距离小于等于d。
排序后单调指针扫,或者直接开桶计数。
Code
1 /** 2 * Codeforces 3 * Problem#940A 4 * Accepted 5 * Time: 15ms 6 * Memory: 2000k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 int n, d; 13 int res; 14 int* ar; 15 16 inline void init() { 17 scanf("%d%d", &n, &d); 18 ar = new int[(n + 1)]; 19 for (int i = 1; i <= n; i++) 20 scanf("%d", ar + i); 21 } 22 23 inline void solve() { 24 sort (ar + 1, ar + n + 1); 25 int r = 1; 26 res = n - 1; 27 for (int i = 1; i <= n; i++) { 28 while (r < n && ar[r + 1] - ar[i] <= d) r++; 29 res = min(res, i + (n - r) - 1); 30 } 31 printf("%d", res); 32 } 33 34 int main() { 35 init(); 36 solve(); 37 return 0; 38 }
Problem A
Codeforces 940B Our Tanya is Crying Out Loud
题目大意
给定一个数$n$和$k$。你有两个操作可以进行
- 将$n$减去1,花费$a$。
- 当$n$是$k$的倍数的时候,将$n$除以$k$,花费$b$。
问将$n$变为1的最小花费
当$n$为$k$的倍数的时候,比较直接除和直接减到$\frac{n}{k}$的花费,如果前者更优就除,否则直接减到1。
当$n$不为$k$的倍数的时候,直接减到$\left \lfloor\frac{n}{k} \right \rfloor k$。
考虑这么做的正确性,唯一这么做的问题是会不会存在多减几次使答案更优的情况。
答案是不会的,因为先除可以避免使用多次的减法(除更优的情况下)。
举个例子来说明,将$k^{2} + k$变为1。
方案1:$k ^ {2} + k \rightarrow k + 1 \rightarrow k \rightarrow 1$
方案2:$k ^ {2} + k \rightarrow k^{2} \rightarrow k \rightarrow 1$
方案1使用了1次加法和2次除法,而方案二使用了$k$次加法和2次除法。
Code
1 /** 2 * Codeforces 3 * Problem#940B 4 * Accepted 5 * Time: 30ms 6 * Memory: 2000k 7 */ 8 #include <bits/stdc++.h> 9 #ifndef WIN32 10 #define Auto "%lld" 11 #else 12 #define Auto "%I64d" 13 #endif 14 using namespace std; 15 16 #define ll long long 17 18 ll n, k, a, b, res = 0; 19 20 inline void init() { 21 scanf(Auto""Auto""Auto""Auto, &n, &k, &a, &b); 22 } 23 24 inline void solve() { 25 if (k == 1) { 26 res = (n - 1) * a; 27 } else { 28 while (n != 1) { 29 if (n < k) { 30 res += (n - 1) * a; 31 break; 32 } else { 33 res += min((n % k) * a + b, (n - n / k) * a); 34 n /= k; 35 } 36 } 37 } 38 printf(Auto"\n", res); 39 } 40 41 int main() { 42 init(); 43 solve(); 44 return 0; 45 }
Problem B
Codeforces 940C Phone Numbers
题目大意
给定一个由小写字母组成且长度为$n$的字符串$s$,要求输出一个字符串$t$,满足:
- $t$中出现的字符在$s$中也出现过
- 它的长度为$k$
- $t$的字典序大于$s$
- 在满足上述条件的所有串中,是字典序最小的一个
题目保证存在解。
当$k > n$时,直接输出字符串$s$,再补上$s$中最小的字符。
否则找到$s$中从第$k$个字符往前找第一个不是最大的字符,把它变为略比它的字符。
然后后面的补最小的字符。
Code
1 /** 2 * Codeforces 3 * Problem#940C 4 * Accepted 5 * Time: 31ms 6 * Memory: 2100k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 int n, k; 13 int mi = 0, ma = 25; 14 boolean exist[26]; 15 char str[100005]; 16 17 inline void init() { 18 scanf("%d%d", &n, &k); 19 memset(exist, false, sizeof(exist)); 20 scanf("%s", str + 1); 21 } 22 23 inline void solve() { 24 for (int i = 1; i <= n; i++) 25 exist[str[i] - ‘a‘] = true; 26 for (mi = 0; !exist[mi]; mi++); 27 for (ma = 25; !exist[ma]; ma--); 28 if (n < k) { 29 printf("%s", str + 1); 30 for (int i = n + 1; i <= k; i++) 31 putchar(mi + ‘a‘); 32 return; 33 } 34 int p; 35 for (p = k; p && str[p] == ma + ‘a‘; p--); 36 str[p]++; 37 while (!exist[str[p] - ‘a‘]) 38 str[p]++; 39 for (p = p + 1; p <= k; p++) 40 str[p] = mi + ‘a‘; 41 str[k + 1] = 0; 42 puts(str + 1); 43 } 44 45 int main() { 46 init(); 47 solve(); 48 return 0; 49 }
Problem C
Codeforces 940D Alena And The Heater
题目大意
给定一个数组$a$,和一个01串$b$,$b$按照如下方式生成(请自行阅读英文)
请你找出一组合法的$l, r$,保证输入存在解。
发现生成方式比较特殊。
于是根据生成方式更新$l, r$即可。
Code
1 /** 2 * Codeforces 3 * Problem#940D 4 * Accepted 5 * Time: 31ms 6 * Memory: 2500k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 11 int n; 12 int a[100005]; 13 char b[100005]; 14 15 inline void init() { 16 scanf("%d", &n); 17 for (int i = 1; i <= n; i++) 18 scanf("%d", a + i); 19 scanf("%s", b + 1); 20 } 21 22 char sameNumber(int p) { 23 char x = b[p - 1]; 24 for (int i = 2; i <= 4; i++) 25 if (b[p - i] != x) 26 return ‘2‘; 27 return x; 28 } 29 30 int l = -1e9, r = 1e9; 31 32 inline void solve() { 33 for (int i = 5; i <= n; i++) { 34 char c = sameNumber(i); 35 if (c == ‘2‘) continue; 36 if (c == b[i]) continue; 37 if (c == ‘1‘) { 38 for (int j = 0; j <= 4; j++) 39 r = min(r, a[i - j] - 1); 40 } else { 41 for (int j = 0; j <= 4; j++) 42 l = max(l, a[i - j] + 1); 43 } 44 } 45 printf("%d %d", l, r); 46 } 47 48 int main() { 49 init(); 50 solve(); 51 return 0; 52 }
Problem D
Codeforces 940E Cashback
题目大意
给定一个长度为$n$的数组和常数$c$,要求你对它进行划分。划分长度为$k$一段的代价是其中所有除去前$\left \lfloor \frac{k}{c} \right \rfloor$小的数的和。定义一个划分的代价是划分的所有段的代价的和。
问最小的代价和。
一段代价可以表示为这一段的和减去前$\left \lfloor \frac{k}{c} \right \rfloor$小的数的和。
那么考虑两段连续的长度为$c$的合并到一起,这样会使得前2小的和变小。所以划分的最长的长度不会超过$2c - 1$
那么考虑长度在$c + 1$和$2c - 1$之间的段。我只需要留一个长度为$c$的段,把它和剩下的分开。这样看的话,划分的一段的长度不会超过$c$。
继续考虑长度小于$c$的段,它的代价直接就是它中间的元素的和。因此可以直接把它分成一些只有1个元素的段。
因此存在一种最优方案满足划分的每一段长度不是1就是$c$。
因此我只用维护连续$c$个元素的最小值,以及它们的和。
于是上单调队列。
时间复杂度$O(n)$。
Code
1 /** 2 * Codeforces 3 * Problem#940E 4 * Accepted 5 * Time: 31ms 6 * Memory: 3700k 7 */ 8 #include <bits/stdc++.h> 9 #ifndef WIN32 10 #define Auto "%lld" 11 #else 12 #define Auto "%I64d" 13 #endif 14 using namespace std; 15 16 #define ll long long 17 18 const int N = 100005; 19 20 int n, c; 21 int st = 1, ed = 0; 22 ll f[N]; 23 int ar[N]; 24 int que[N]; 25 26 inline void init() { 27 scanf("%d%d", &n, &c); 28 for (int i = 1; i <= n; i++) 29 scanf("%d", ar + i); 30 } 31 32 inline void solve() { 33 34 f[1] = 0; 35 ll sum = 0; 36 for (int i = 1; i <= n; i++) { 37 while (ed >= st && ar[que[ed]] >= ar[i]) ed--; 38 que[++ed] = i; 39 f[i] = f[i - 1] + ar[i]; 40 sum += ar[i]; 41 while (ed >= st && que[st] <= i - c) st++; 42 if (i >= c) 43 sum -= ar[i - c], f[i] = min(f[i], f[i - c] + sum - ar[que[st]]); 44 } 45 46 printf(Auto"\n", f[n]); 47 } 48 49 int main() { 50 init(); 51 solve(); 52 return 0; 53 }
Problem E
原文地址:https://www.cnblogs.com/yyf0309/p/Codeforces_Round_466_Div_2.html