Codeforces Round #555 (Div. 3)
贪心+模拟大法好!
A、Reachable Numbers
思路:简单模拟。不难发现,通过f函数运算下去的步骤数量不会很多,所以暴力模拟,只要有一个数字第二次出现break即可。
AC代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <iostream> 6 #include <algorithm> 7 #include <iomanip> 8 #include <complex> 9 #include <string> 10 #include <vector> 11 #include <set> 12 #include <map> 13 #include <list> 14 #include <deque> 15 #include <queue> 16 #include <stack> 17 #include <bitset> 18 using namespace std; 19 typedef long long LL; 20 typedef unsigned long long ULL; 21 const int dir[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; // 上右下左 22 const int mx[8] = {-1, -2, -2, -1, 1, 2, 2, 1}; // 马可走的八个方向 23 const int my[8] = {-2, -1, 1, 2, 2, 1, -1, -2}; 24 const double eps = 1e-6; 25 const double PI = acos(-1.0); 26 const int maxn = 1e5+5; 27 const int inf = 0x3f3f3f3f; 28 29 LL n; 30 set<LL> st; 31 32 int main() { 33 while(cin >> n) { 34 st.clear(); 35 st.insert(n); 36 while(1) { 37 ++n; 38 while(n % 10 == 0) n /= 10; 39 if(st.count(n)) break; 40 st.insert(n); 41 } 42 cout << st.size() << endl; 43 } 44 return 0; 45 }
B、Long Number
思路:简单贪心+模拟。为了使得替换得到的数字最大,并且替换的数字必须是连续的,我们只需找到第一个替换的数字满足 $c_i > b_i$ 作为贪心的起点,并且让$ b_i > c_i$ 作为贪心的终点,然后简单模拟一下即可。
AC代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <iostream> 6 #include <algorithm> 7 #include <iomanip> 8 #include <complex> 9 #include <string> 10 #include <vector> 11 #include <set> 12 #include <map> 13 #include <list> 14 #include <deque> 15 #include <queue> 16 #include <stack> 17 #include <bitset> 18 using namespace std; 19 typedef long long LL; 20 typedef unsigned long long ULL; 21 const int dir[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; // 上右下左 22 const int mx[8] = {-1, -2, -2, -1, 1, 2, 2, 1}; // 马可走的八个方向 23 const int my[8] = {-2, -1, 1, 2, 2, 1, -1, -2}; 24 const double eps = 1e-6; 25 const double PI = acos(-1.0); 26 const int maxn = 2e5+5; 27 const int inf = 0x3f3f3f3f; 28 29 int n, f[10], b[maxn], c[maxn]; 30 string str, ans; 31 bool flag; 32 33 34 int main() { 35 while(cin >> n) { 36 cin >> str; 37 ans = ""; 38 flag = false; 39 for(int i = 0; i < n; ++i) b[i] = str[i] - '0'; 40 for(int i = 1; i <= 9; ++i) cin >> f[i]; 41 for(int i = 0; i < n; ++i) c[i] = f[b[i]]; 42 for(int i = 0; i < n; ++i) { 43 if(!flag) { 44 if(b[i] < c[i]) flag = true, ans += c[i] + '0'; // 贪心起点 45 else ans += str[i]; 46 }else { 47 if(b[i] > c[i]) { // 贪心终点 48 for(int j = i; j < n; ++j) ans += str[j]; 49 break; 50 } 51 else ans += c[i] + '0'; 52 } 53 } 54 cout << ans << endl; 55 } 56 return 0; 57 }
C1、Increasing Subsequence (easy version)
思路:简单模拟即可。
AC代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <iostream> 6 #include <algorithm> 7 #include <iomanip> 8 #include <complex> 9 #include <string> 10 #include <vector> 11 #include <set> 12 #include <map> 13 #include <list> 14 #include <deque> 15 #include <queue> 16 #include <stack> 17 #include <bitset> 18 using namespace std; 19 typedef long long LL; 20 typedef unsigned long long ULL; 21 const int dir[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; // 上右下左 22 const int mx[8] = {-1, -2, -2, -1, 1, 2, 2, 1}; // 马可走的八个方向 23 const int my[8] = {-2, -1, 1, 2, 2, 1, -1, -2}; 24 const double eps = 1e-6; 25 const double PI = acos(-1.0); 26 const int maxn = 2e5+5; 27 const int inf = 0x3f3f3f3f; 28 29 int n, lt, rt, cnt, per, a[maxn]; 30 string str; 31 32 int main() { 33 while(cin >> n) { 34 cnt = 0, per = 0; 35 str = ""; 36 for(int i = 0; i < n; ++i) cin >> a[i]; 37 int i, j; 38 for(i = 0, j = n - 1; i < j;) { 39 if(a[i] > a[j]) { 40 if(a[j] > per) str += 'R', ++cnt, per = a[j], --j; 41 else if(a[i] > per) str += 'L', ++cnt, per = a[i], ++i; 42 else break; 43 }else { 44 if(a[i] > per) str += 'L', ++cnt, per = a[i], ++i; 45 else if(a[j] > per) str += 'R', ++cnt, per = a[j], --j; 46 else break; 47 } 48 } 49 if(i == j && a[j] > per) { 50 if(2 * j >= n - 1) str += 'R', ++cnt; 51 else str += 'L', ++cnt; 52 } 53 cout << cnt << endl; 54 cout << str << endl; 55 } 56 return 0; 57 }
C2、Increasing Subsequence (hard version)
思路:贪心+简单模拟。①若当前两边i,j指向的数字不同,则直接贪心处理;②若当前两边i,j指向的数字相同,如何选择左边或右边呢?我们只需另外设置2个变量来比较i和j分别能延伸的长度,哪边长就贪心选哪边即可。
AC代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <iostream> 6 #include <algorithm> 7 #include <iomanip> 8 #include <complex> 9 #include <string> 10 #include <vector> 11 #include <set> 12 #include <map> 13 #include <list> 14 #include <deque> 15 #include <queue> 16 #include <stack> 17 #include <bitset> 18 using namespace std; 19 typedef long long LL; 20 typedef unsigned long long ULL; 21 const int dir[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; // 上右下左 22 const int mx[8] = {-1, -2, -2, -1, 1, 2, 2, 1}; // 马可走的八个方向 23 const int my[8] = {-2, -1, 1, 2, 2, 1, -1, -2}; 24 const double eps = 1e-6; 25 const double PI = acos(-1.0); 26 const int maxn = 2e5+5; 27 const int inf = 0x3f3f3f3f; 28 29 int n, res1, res2, cnt, per, a[maxn]; 30 string str; 31 bool flag; 32 33 int main() { 34 while(cin >> n) { 35 cnt = 0, per = 0; 36 str = ""; 37 for(int i = 0; i < n; ++i) cin >> a[i]; 38 int i, j; 39 for(i = 0, j = n - 1; i < j;) { 40 if(a[i] > a[j]) { 41 flag = false; 42 if(a[j] > per) str += 'R', ++cnt, per = a[j], --j, flag = true; 43 if(flag) continue; 44 if(a[i] > per) str += 'L', ++cnt, per = a[i], ++i, flag = true; 45 if(!flag) break; 46 }else if(a[j] > a[i]){ 47 flag = false; 48 if(a[i] > per) str += 'L', ++cnt, per = a[i], ++i, flag = true; 49 if(flag) continue; 50 if(a[j] > per) str += 'R', ++cnt, per = a[j], --j, flag = true; 51 if(!flag) break; 52 } 53 else { // equal 54 if(a[i] <= per) break; 55 res1 = res2 = 0; 56 for(int lt = i; lt + 1 < j && a[lt] < a[lt + 1]; ++lt, ++res1); 57 for(int rt = j; rt - 1 > i && a[rt - 1] > a[rt]; --rt, ++res2); 58 if(res1 > res2) str += 'L', ++cnt, per = a[i], ++i; 59 else str += 'R', ++cnt, per = a[j], --j; 60 } 61 } 62 if(i == j && a[j] > per) { 63 if(2 * j >= n - 1) str += 'R', ++cnt; 64 else str += 'L', ++cnt; 65 } 66 cout << cnt << endl; 67 cout << str << endl; 68 } 69 return 0; 70 }
D、N Problems During K Days
思路:简单思维+贪心。题意就是要求构造出一个含k个元素的序列满足该序列①是递增的,②其和刚好为n,③ $ a_i < a_{i + 1} \le 2 a_i $。第一直觉就是先给这k个元素分别有序地填上1~k这k个数字,这样一定满足①和③条件,接着自然就想到一个"NO"点和一个"YES"点,即 $\frac{k(k+1)}{2} > n $、当k==1时a[0] =n。剩下的如何分配呢?借助填补1~k这一个“顺子”(来源斗地主)的思想(若是能按“顺子”填补,则一定能满足③这个条件),我们把剩下的 $ now1 = n - \frac{k(k+1)}{2} $ 分成 $ now2 = \frac{now1}{k} $ 组,然后再给每个$a_i$分别都加上 now2,同样得到一个含k个元素的“顺子”(两两之差的绝对值都为1)!剩下1的个数为 $ now3 = now1 \% k $ (不超过k-1)个,为了满足条件①和③,我们从后往前依次贪心给每个 $ a_i $ 加1,加到now3个1就break!不难证明这一定满足条件③。但是!!!有个trick需要特判:也就是剩下1的个数刚好为k-1个的情况:1、当 k ==2时,若a[0] == 1,说明a[0] * 2 = 2 < a[1] = 3(显然不成立),而其余情况一定成立;2、当 $ k \geq 3 $ 时,先--a[1],++a[k - 1],这个操作不难验证也是正确的。但当 $ k == 3 $ 时,还需要检验1种特殊情况:1、3、4,因为将3减1变成2,4加1变成5后该序列为1,2,5,可知这序列一定不满足情况,其余一定能满足所有条件,这个不难验证!!!
AC代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <iostream> 6 #include <algorithm> 7 #include <iomanip> 8 #include <complex> 9 #include <string> 10 #include <vector> 11 #include <set> 12 #include <map> 13 #include <list> 14 #include <deque> 15 #include <queue> 16 #include <stack> 17 #include <bitset> 18 using namespace std; 19 typedef long long LL; 20 typedef unsigned long long ULL; 21 const int dir[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; // 上右下左 22 const int mx[8] = {-1, -2, -2, -1, 1, 2, 2, 1}; // 马可走的八个方向 23 const int my[8] = {-2, -1, 1, 2, 2, 1, -1, -2}; 24 const double eps = 1e-6; 25 const double PI = acos(-1.0); 26 const int maxn = 1e5+5; 27 const int inf = 0x3f3f3f3f; 28 29 LL n, k, now1, now2, now3, a[maxn]; 30 31 int main() { 32 while(cin >> n >> k) { 33 if((1LL + k) * k / 2 > n) {cout << "NO" << endl; continue;} 34 if(k == 1) {cout << "YES\n" << n << endl; continue;} 35 now1 = n - (1LL + k) * k / 2; 36 now2 = now1 / k; 37 now3 = now1 % k; 38 for(int i = 0; i < k; ++i) a[i] = i + 1 + now2; 39 for(int i = k - 1, num = now3; k > 0 && num; --num, --i) ++a[i]; 40 41 if(now3 != k - 1) { 42 cout << "YES" << endl; 43 for(int i = 0; i < k; ++i) cout << a[i] << " \n"[i == k - 1]; 44 }else { 45 if(k >= 3) --a[1], ++a[k - 1]; 46 if((k == 2 && a[0] == 1) || (k == 3 && a[1] * 2 < a[2])) cout << "NO" << endl; 47 else { 48 cout << "YES" << endl; 49 for(int i = 0; i < k; ++i) cout << a[i] << " \n"[i == k - 1]; 50 } 51 } 52 } 53 return 0; 54 }
E、Minimum Array
思路:简单贪心。这个题用vector容器会T到飞qwq,题解是用关联式容器multiset(自动排序),其增删查的时间复杂度均为 $O(log^n) $,用起来非常简单,容易上手!注意到a数组、b数组中所有元素都小于n,那么问题求解就变得简单了:为了使得每个 $a_i$ 各自能匹配到的 $b_j$ 使得 $(a_i + b_j) \% n $ 最小,只需贪心找不小于 $ (n - a_i) $ 的最小值,因为以 $ (n - a_i) $ 为起点的$ b_j$ 和 $ a_i$ 的和取模n的余数为0,往后逐渐增加,循环一圈到 $ (n - a_i - 1) $ 达到最大;若找不到,则取容器中首个元素进行匹配,即离 $ (n-a_i) $ 左边最远的一个,不难证明此时的 $(a_i+b_j) \% n $ 是最小的。时间复杂度为 $O(nlog^n)$。
AC代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cmath> 5 #include <iostream> 6 #include <algorithm> 7 #include <iomanip> 8 #include <complex> 9 #include <string> 10 #include <vector> 11 #include <set> 12 #include <map> 13 #include <list> 14 #include <deque> 15 #include <queue> 16 #include <stack> 17 #include <bitset> 18 using namespace std; 19 typedef long long LL; 20 typedef unsigned long long ULL; 21 const int dir[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; // 上右下左 22 const int mx[8] = {-1, -2, -2, -1, 1, 2, 2, 1}; // 马可走的八个方向 23 const int my[8] = {-2, -1, 1, 2, 2, 1, -1, -2}; 24 const double eps = 1e-6; 25 const double PI = acos(-1.0); 26 const int maxn = 2e5+5; 27 const int inf = 0x3f3f3f3f; 28 29 30 int n, a[maxn], b, pos, siz; 31 multiset<int> st; 32 multiset<int>::iterator it; 33 34 35 int main() { 36 while(~scanf("%d", &n)) { 37 st.clear(); 38 for(int i = 0; i < n; ++i) scanf("%d", &a[i]); 39 for(int i = 0; i < n; ++i) scanf("%d", &b), st.insert(b); 40 for(int i = 0; i < n; ++i) { 41 it = st.lower_bound(n - a[i]); 42 if(it == st.end()) it = st.begin(); 43 printf("%d%c", (a[i] + *it) % n, " \n"[i == n - 1]); 44 st.erase(it); 45 } 46 } 47 return 0; 48 }