Codeforces #640 div4 A - G 解题报告
A. Sum of Round Numbers
\(Description:\)
给你一个数 \(n\),将其拆分为几个数的和,这几个数满足除最高位外,其余位全为 \(0\)。
\(Solve:\)
我直接以字符串读入,然后扫描一遍记下答案即可。
\(Code:\)
#include <bits/stdc++.h>
using namespace std;
int main(){
int t; cin >> t;
while(t --){
char num[10];
scanf("%s", num + 1);
vector<string> ans;
int len = strlen(num + 1);
for(int i = 1; i <= len; i ++){
if(num[i] == '0') continue; // 是 0 就跳过
string tmp = "";
tmp += num[i];
for(int j = 1; j <= len - i; j ++) tmp += '0'; // 加 0
ans.push_back(tmp);
}
cout << ans.size() << endl;
for(int i = 0; i < ans.size(); i ++) cout << ans[i] << " ";
puts("");
}
return 0;
}
\(\\\)
B. Same Parity Summands
\(Description:\)
给你 \(n, k\) 两个数,问是否存在数组 \(a\) 满足 \(n = a_1 + a_2 + a_3 +...+a_k\) ?\(a\) 里的数要么都是奇数,要么都是偶数。
\(Solve:\)
根据 \(n, k\) 的奇偶性来分类讨论即可。
\(Code:\)
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int a[N];
int main(){
int t; cin >> t;
while(t --){
int n, k; cin >> n >> k;
if(n & 1){
if(k & 1){
if(n < k) { puts("NO"); continue; } // 即使全为 1 都大于 n
for(int i = 1; i < k; i ++) a[i] = 1;
a[k] = n - k + 1;
}else{ // k 是偶数不合法
puts("NO"); continue;
}
}else{
if(k & 1){
if(n / 2 < k) { puts("NO"); continue; } // 是偶数每一项至少为 2
for(int i = 1; i < k; i ++) a[i] = 2;
a[k] = n - (k - 1) * 2;
}else{
// 这种情况下,每个数为奇数更优
if(n < k) { puts("NO"); continue; }
for(int i = 1; i < k; i ++) a[i] = 1;
a[k] = n - k + 1;
}
}
puts("YES");
for(int i = 1; i <= k; i ++) printf("%d ", a[i]);
puts("");
}
return 0;
}
\(\\\)
C. K-th Not Divisible by n
\(Description:\)
给出 \(n, k\),输出不能被 \(n\) 整除的第 \(k\) 个数。
\(Solve:\)
对于每 \(n\) 个数,都有 \(n - 1\) 个数不能被 \(n\) 整除,那么根据这个我们可以轻易算出第 \(k\) 个数。
\(Code:\)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
int t; cin >> t;
while(t --){
ll n, k; cin >> n >> k;
ll t = k / (n - 1);
ll c = k - t * (n - 1);
long long ans = t * n + (c > 0 ? c : -1); // c = 0 就必须 -1
cout << ans << endl;
}
return 0;
}
\(\\\)
D. Alice, Bob and Candies
\(Description:\)
两个人轮流吃数组中的元素,一个从左往右,一个从右往左,第一次只吃 \(a[i]\),每次吃掉的元素和要严格大于上一个人吃的,求吃了几次和每个人吃了多少?
\(Solve:\)
直接按照题意模拟即可。
\(Code:\)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
int arr[N];
int main(){
int t; cin >> t;
while(t --){
int n; cin >> n;
for(int i = 1; i <= n; i ++) scanf("%d", &arr[i]);
int l = 1, r = n, a = 0, b = 0;
int prev_a = 0, prev_b = 0; // 上次吃的总和
int i; // 移动次数
for(i = 1; ; i ++){
if(i & 1){
while(l <= r){
a += arr[l];
prev_a += arr[l ++];
if(prev_a > prev_b){
prev_b = 0;
break;
}
}
}else{
while(l <= r){
b += arr[r];
prev_b += arr[r --];
if(prev_b > prev_a){
prev_a = 0;
break;
}
}
}
if(l > r) break;
}
printf("%d %d %d\n", i, a, b);
}
return 0;
}
\(\\\)
E. Special Elements
\(Description:\)
长度为 \(n\) 的数组 \(a\),存在多少 \(a_i = a_l + a_{l+1} + ... + a_r(1\leq l < r\leq n,a_i \leq n)\) ? 相同元素也要记入答案中。
\(Solve:\)
预处理出前缀和数组,然后来枚举区间 \([l, r]\) ,记录答案即可。
\(Code:\)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4;
int a[N], sum[N], m[N];
int main(){
int t; cin >> t;
while(t --){
memset(m, 0, sizeof m);
int n; cin >> n;
sum[0] = 0;
for(int i = 1; i <= n; i ++){
scanf("%d", &a[i]);
m[a[i]] ++; // 记录每个数出现的次数
sum[i] = sum[i - 1] + a[i]; // 前缀和
}
int ans = 0;
for(int l = 1; l <= n; l ++)
for(int r = l + 1; r <= n; r ++){
int t = sum[r] - sum[l - 1];
if(t > n) continue; // 每个数都是 <= n,> n 显然不合法
ans += m[t];
m[t] = 0; // 加了必须清空
}
cout << ans << endl;
}
return 0;
}
\(\\\)
F. Binary String Reconstruction
\(Description:\)
对于一个二进制字符串 \(s\),给出 \(n_0, n_1, n_2\),分别代表在 \(n - 1\) 对相邻字符组里面全是 \(0\),有 \(1\) 有 \(0\),全是 \(1\) 的个数。输出满足一个这样条件的 \(s\)。
\(Solve:\)
显然 \(len[s] = n_0 + n_1 + n_2 + 1\)。直接暴力构造即可。左边为全 \(0\),中间为 \(0, 1\),右边为 \(1\) 即可。注意要判断一下 \(n_1\) 的奇偶性。
\(Code:\)
#include <bits/stdc++.h>
using namespace std;
int main(){
int t; cin >> t;
while(t --){
int n0, n1, n2; cin >> n0 >> n1 >> n2;
string s = "";
if(n1 == 0){ // 特判
for(int i = 1; i <= n0; i ++) s += '0';
for(int i = 1; i <= n2; i ++) s += '1';
s += n0 ? '0' : '1';
cout << s << endl;
continue;
}
for(int i = 1; i <= n0; i ++) s += '0';
if(n1 & 1){
for(int i = 1; i <= (n1 + 1) / 2; i ++){
s += '0'; s += '1';
}
}else{
for(int i = 1; i <= n1 / 2; i ++){
s += '0'; s += '1';
}
}
for(int i = 1; i <= n2; i ++) s += '1';
if((n1 & 1) == 0) {
s += '0';
}
cout << s << endl;
}
return 0;
}
\(\\\)
G. Special Permutation
\(Description:\)
是否存在一种 \([1, n]\) 的 排列使得相邻两个数差值的绝对值在 \([2, 4]\) 之间?
\(Solve:\)
显然 \(n < 4\) 是无解的。
所以我的想法是在 \(n = 4\) 基础上推出其他的。
如果 \(n\) 是奇数,就加在上一项的后面;如果 \(n\) 是偶数,就在中间找一个可以插入的地方,是一定存在的。
理由,奇数显然是一定可以的,对于偶数的情况下,我们可以发现,对于 \(n = 6\) 的情况,是插入在 \((4, 2)\) 之间的,那么就会存在 \((4, 6)\),那么对于 \(n = 8\) 的情况就可以插入在 \((4, 6)\) 之间,对于 \(n = 10\) 的情况就可以插入在 \((6, 8)\) 之间,依次类推,对于 \(n\) (偶数)来说,可以插入在 \((n - 4, n - 2)\) 之间,所以是一定有解的。
\(Code:\)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
vector<int> vec[N]; // vec[i] 代表 n = i 时的合法排列
// 预处理
void init(){
vec[4] = {3, 1, 4, 2};
for(int i = 5; i < N; i ++){
if(i & 1){ // 奇数直接加在上一项后面
vec[i] = vec[i - 1];
vec[i].push_back(i);
}else{
int mark = 0; // 标记是否加了 i
for(int j = 0; j < vec[i - 1].size(); j ++){
int num = vec[i - 1][j];
vec[i].push_back(num);
// 判断 vec[i - 1][j], i, vec[i - 1][j + 1] 这样是否合法,合法就插入
if(!mark && abs(i - num) >= 2 && abs(i - num) <= 4 && j < i - 2 && abs(i - vec[i-1][j+1]) >= 2 && abs(i - vec[i-1][j + 1]) <= 4){
vec[i].push_back(i);
mark = 1;
}
}
}
}
}
int main(){
init();
int t; cin >> t;
while(t --){
int n; cin >> n;
if(n <= 3) { puts("-1"); continue; }
for(int i = 0; i < n; i ++) cout << vec[n][i] << " ";
puts("");
}
return 0;
}