复健训练-CF1706(Codeforces Round #809 (Div. 2))
A. Another String Minimization Problem
题意:给一个长为 m 的全是 'B' 的字符串,给 n 个操作,每个操作有个 $a_i$ ,表示可以把字符串中第 $a_i$ 位或者 $m-a_i+1$ 位变成 'A'。问字典序最小的字符串。
做法:能变得就变,先变前面再变后面。
#include<bits/stdc++.h> #define rep(i,x,y) for (int i=(x);i<=(y);i++) #define ll long long using namespace std; const int N = 105; int n, m, a[N], b[N]; void Main(){ scanf("%d%d", &n, &m); rep (i,1,n) scanf("%d", &a[i]); rep (i,1,m) b[i] = 1; rep (i,1,n){ if (b[min(a[i], m-a[i]+1)] == 0) b[max(a[i], m-a[i]+1)] = 0; else b[min(a[i], m-a[i]+1)] = 0; } rep (i,1,m) cout<<(char)(b[i] + 'A'); puts(""); return; } int main(){ int T; scanf("%d", &T); while (T--) Main(); return 0; }
B. Making Towers
题意:有 n 个方块,每个方块有一个颜色。现在第一个方块在 (0, 0) ,每个方块放在前一个方块的左边、右边或者上面。如果有一列竖着全都是同一种颜色,那么说构成了这种颜色的 tower 。现在问对于每一种颜色,问最多能搭出该颜色多高的 tower 。
做法:可以发现在一个序列中,如果两个相同颜色中间差了偶数个别的方块,他们就可以搭在一起。差偶数个方块相当于是下标奇偶性不同。所以用一个 $f[0/1][i]$ 表示颜色 i 能搭出多高的塔,$0/1$ 是其所在下标奇偶性,转移就是 $f[t][i] = f[t \oplus 1][i] + 1$ 。
#include<bits/stdc++.h> #define rep(i,x,y) for (int i=(x);i<=(y);i++) #define ll long long using namespace std; const int N = 1e5+10; int n, a[N], f[2][N], ans[N]; void Main(){ scanf("%d", &n); rep (i,1,n) ans[i] = f[0][i] = f[1][i] = 0; rep (i,1,n){ scanf("%d", &a[i]); f[i&1][a[i]] = f[(i&1)^1][a[i]] + 1; ans[a[i]] = max(ans[a[i]], f[i&1][a[i]]); } rep (i,1,n) printf("%d ", ans[i]); puts(""); return; } int main(){ int T; scanf("%d", &T); while (T--) Main(); return 0; }
C. Qpwoeirut And The City
题意:每个 building 有 $h_i$ 层楼,定义一个 building 是 cool 的当且仅当它满足 $h_i>h_{i-1}$ 且 $h_i > h_{i+1}$ (两边的都不是 cool building)。现在可以增加层数(不能减少),问达到最多 cool 的 building 数,最少总共需要加多少层。
做法:n 为奇数,肯定是偶数位的都是 cool ,直接算。 n 为偶数,最多的 cool 数量应该是 $n/2-1$,要达到这个数目,如果当前是 cool ,那么前一个肯定不是 cool ;如果当前非 cool ,那么要么前一个是 cool,要么前前个是 cool ,而且前前个是 cool 的情况最多出现一次。然后我用了一个 dp , $f[i][0/1][0/1]$ 表示到第 i 个 building ,当前是否是 cool ,有没有出现过前前个是 cool 的情况。转移见代码。
#include<bits/stdc++.h> #define rep(i,x,y) for (int i=(x);i<=(y);i++) #define ll long long using namespace std; const int N = 1e5+10; const ll inf = 1e18; int n, a[N]; ll ans, f[N][2][2]; void Main(){ scanf("%d", &n); rep (i,1,n) scanf("%d", &a[i]); if (n&1){ ans = 0; for (int i=2; i<n; i+=2) ans += max(max(a[i-1], a[i+1]) + 1 - a[i], 0); printf("%lld\n", ans); } else{ rep (i,1,n) f[i][0][0] = f[i][1][0] = f[i][0][1] = f[i][1][1] = inf; f[1][0][0] = 0; rep (i,2,n){ if (i < n){ f[i][1][0] = f[i-1][0][0] + max(max(a[i-1], a[i+1]) + 1 - a[i], 0); f[i][1][1] = f[i-1][0][1] + max(max(a[i-1], a[i+1]) + 1 - a[i], 0); } f[i][0][0] = f[i-1][1][0]; f[i][0][1] = f[i-1][1][1]; if (i>2) f[i][0][1] = min(f[i-2][1][0], f[i][0][1]); if (i==2) f[i][0][1] = min(f[i][0][1], f[i-1][0][0]); } printf("%lld\n", min(f[n][0][0], f[n][0][1])); } return; } int main(){ int T; scanf("%d", &T); while (T--) Main(); return 0; }
D1. Chopping Carrots (Easy Version)
题意:给定 n, k 以及 ${a_i}$ ,要求构造一个 $p_i \in {1,k}$ ,使得 $cost = \max{\lfloor \frac{a_i}{p_i} \rfloor} - \min{\lfloor \frac{a_i}{p_i} \rfloor}$ 最小,输出最小的 cost 。 $n,k,a_i \le 3000$
做法:从 0 到 a[1] 枚举 $\min{\lfloor \frac{a_i}{p_i} \rfloor}$ ,那么只需最小化 max 即可。对于每个 a[i] ,有了 min 这个 max 的最小值其实可以算出来就是 $a[i]/ (a[i]/i)$ ,当然 i = 0 要特判。所以直接对这些取 max 值取 max ,计算答案即可。
#include<bits/stdc++.h> #define rep(i,x,y) for (int i=(x);i<=(y);i++) using namespace std; const int N = 3005; int n, m, a[N]; void Main(){ scanf("%d%d", &n, &m); rep (i,1,n) scanf("%d", &a[i]); int ans = 3000; rep (i,0,a[1]){ int mx = 0, p; rep (j,1,n) p = min(m, i ? (a[j]/i) : (a[j]+1)), mx = max(mx, a[j]/p); ans = min(mx - i, ans); } printf("%d\n", ans); return; } int main(){ int T; scanf("%d", &T); while (T--) Main(); return 0; }
D2. Chopping Carrots (Hard Version)
题意:和上一题一样,$n,k,a_i \le 1e5$
做法:不能每个数都算它的 max 值了。考虑对于每个 $a_i$ , $\lfloor \frac{a_i}{p_i} \rfloor$ 的不同值最多只有 $\sqrt{a_i}$ 个,然后我们依然是枚举 $v = \min{\lfloor \frac{a_i}{p_i} \rfloor}$ ,那么相应的 max 值其实是那根号个值里面最小的 $\ge v$ 的值。例如对于某一个 $a_i$ ,其中两个值为 $s_1, s_2 (s_1<s_2)$ ,那么对于 min 值在 $[s_1 + 1, s_2]$ 内它的 max 值都是 $s_2$ 。所以我们保存一个数组 $p[i]$ 表示当 min 值为 i 的时候 max 值应该是多少,相当于对于每个数要一个区间 max ,但这个可以在 $s_1$ 处打标记最后再求前缀 max。求完前缀 max 以后 $p[i]$ 就是对 i 这个最小值的最小的 max 值,求答案即可。
#include<bits/stdc++.h> #define rep(i,x,y) for (int i=(x);i<=(y);i++) using namespace std; const int N = 1e5+10; int n, m, a[N], p[N]; void Main(){ scanf("%d%d", &n, &m); int Mx = 0; rep (i,1,n){ scanf("%d", &a[i]); Mx = max(Mx, a[i]); int last = 1e9; for (int j = 1; j <= min(a[i], m); j = (a[i]/(a[i]/j)) + 1){ //遍历出现不同的 ai/pi 的 pi int v = a[i]/j; p[v + 1] = max(p[v + 1], last); last = v; } p[0] = max(p[0], last); } // printf("%d\n", p[0]); int ans = 1e9, mx = 0; rep (i,0,a[1]){ mx = max(mx, p[i]); ans = min(ans, mx - i); } printf("%d\n", ans); rep (i, 0, Mx+1) p[i] = 0; return; } int main(){ int T; scanf("%d", &T); while (T--) Main(); return 0; }
E 咕咕咕