【Codeforces Round #655 (Div. 2)】A-D
Codeforces Round #655 (Div. 2)
A. Omkar and Completion
题意
给出一个整数 n ,让构造出一个长度为 n 的数组,使得不存在\(1\leq x,y,z\leq n\),满足\(a_x+a_y=a_z\)。
思路
直接输出 n 个 1
代码
#include <bits/stdc++.h>
#define fuck system("pause")
#define emplace_back push_back
#define pb push_back
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int N = 2e5 + 10;
int main()
{
int T;
scanf("%d", &T);
while(T--){
int n;
scanf("%d", &n);
for (int i = 1; i <= n;i++){
printf("1 ");
}
printf("\n");
}
// fuck;
return 0;
}
/*
*/
B. Omkar and Last Class of Math
题意
给出一个整数 n ,让找出两个整数 a , b ,满足 a + b = n ,并且 LCM(a,b) 最小。
思路
先猜一下,偶数应该直接输出两个半值。
直接打了个表:
找出其最小的素因子 p ,输出 n / p , n - n / p。
代码
#include <bits/stdc++.h>
#define fuck system("pause")
#define emplace_back push_back
#define pb push_back
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int N = 2e5 + 10;
int main()
{
int T;
scanf("%d", &T);
while (T--) {
int n, flag = 0;
scanf("%d", &n);
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
flag = i;
break;
}
}
if (!flag) {
printf("%d %d\n", 1, n - 1);
} else {
printf("%d %d\n",n/flag, n-n/flag);
}
}
// fuck;
return 0;
}
C. Omkar and Baseball
题意
给出一个长度为 n 的全排列,现在定义一种特殊的交换:
你可以选择一个连续的子数组,任意排放其中元素的位置,只需保证每个元素都不在本来的位置。
问最少需要多少次排列使得其递增。
思路
首先可以知道最多只需要排两次。
如果本来递增,那么不需要排。
如果只有一段连续的子数组,每个元素都不在其位置上( i 不在第 i 个位置),那么只需要一次。
有两段或者多段,那么第一次选择整个数组进行打乱,第二次维护成递增
代码
#include <bits/stdc++.h>
#define fuck system("pause")
#define emplace_back push_back
#define pb push_back
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const double eps = 1e-6;
const int inf = 0x3f3f3f3f;
const int N = 2e5 + 10;
int arr[N];
int main()
{
int T;
scanf("%d", &T);
while (T--) {
int n;
scanf("%d", &n);
int flag = 1, num = 0;
for (int i = 1; i <= n; i++) {
scanf("%d", &arr[i]);
if (arr[i] < arr[i - 1]) {
flag = 0;
}
}
if (flag) {
printf("0\n");
continue;
}
int pre, nex;
for (int i = 1; i <= n; i++) {
if (arr[i] != i) {
pre = i;
break;
}
}
for (int i = n; i; i--) {
if (arr[i] != i) {
nex = i;
break;
}
}
for (int i = pre; i <= nex; i++) {
if (arr[i] == i) {
flag = 1;
break;
}
}
if (flag) {
printf("2\n");
} else
printf("1\n");
}
// fuck;
return 0;
}
/*
*/
D. Omkar and Circle
题意
给出一个长度为 n(奇数) 的循环数组(首尾相连),现在每次可以选择两个相距为 2 的数字,用他俩的和替代中间的数字。
操作到最后,只剩下一个数字,问这个数字最大是多少。
思路
本质就是删除 n / 2 个数字后最大的和。
删除的时候要满足这些数字不能相邻。
试着删一下就可以发现:
无论怎么删除都会存在两个连续的数字没有被删除,这两个数字相邻的必定被删除,其他的数字隔一个删一个。
所以我们可以枚举这两个没有被删除的数字。
维护两个数组 pre , suf 。
\(pre_i\)表示以第 i 个为结尾的隔一个删一个的被删除的和。
\(suf_i\)表示以第 i 个为开头的隔一个删一个的被删除的和。
对于以第 i , i + 1 没有被删除,其剩余的和为 \(sum - pre_i - suf_{i+2}\)。
注意特判第 1 个和最后 1 个以及倒数第一个倒数第二个没被删除。
代码
#include <bits/stdc++.h>
#define fuck system("pause")
#define emplace_back push_back
#define pb push_back
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const double eps = 1e-6;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10;
int arr[N];
ll pre[N], suf[N];
int main()
{
int n;
ll sum = 0;
scanf("%d", &n);
for (int i = 1; i <= n;i++){
scanf("%d", &arr[i]);
sum += arr[i];
}
pre[1] = arr[1], suf[n] = arr[n];
for (int i = 2; i <= n;i++){
pre[i] = pre[i - 2] + arr[i];
}
for (int i = n - 1; i;i--){
suf[i] = suf[i + 2] + arr[i];
}
ll ans = inf;
for (int i = 1; i < n - 1; i++) {
ans = min(ans, pre[i - 1] + suf[i + 2]);
}
ans = min(min(pre[n - 1], pre[n - 2]), ans);
printf("%lld\n", sum - ans);
// fuck;
return 0;
}
/*
*/