- Codeforces Round #655 (Div. 2)
- 1. 题目分析
- 2. 题解
- A. Omkar and Completion
- B. Omkar and Last Class of Math
- C. Rational Ratio
- D. Omkar and Circle
- E. Omkar and Last Floor
- F. Omkar and Modes
Codeforces Round #655 (Div. 2)
1. 题目分析
- A: 思维题,构造性题目
- B: 打表发现规律
- C: 思维
- D: 思维+dp
- E:
- F:
2. 题解
A. Omkar and Completion
题意: 给定t个测试样例,每个测试样例给定一个n,要求构造一个长度为n的数列,数列中任意两个数字相加不会等于数列中其他的数字。t~1e3, n~1e3
题解: 本题是构造性的题目,没说数列中的数字要满足什么条件,如果全为1即可
代码:
#include
using namespace std;
int main() {
int T, n;
cin >> T;
while (T--) {
cin >> n;
for (int i = 1; i <= n; ++i) {
cout << 1 << " ";
}
cout << endl;
}
return 0;
}
B. Omkar and Last Class of Math
题意: 给定一个n,可以把n拆解成a、b,使得a+b=n,求使得lcm(a, b)最小的a和b。n~1e9
题解: 打表发现,如果n是偶数,那么直接输出n/2 n/2;如果n是奇数,那么去找n的最大奇数因数a,输出a n-a
代码
#include
using namespace std;
int main() {
int T;
cin >> T;
for (int i = 1, a; i <= T; ++i) {
scanf("%d", &a);
if (a % 2 == 0) {
cout << a / 2 << " " << a / 2 << endl;
}
else {
int maxv = 1;
for (int i = 2; i <= a / i; ++i) {
if (i % 2 == 0) continue;
if (a % i == 0 && (i > maxv || a / i > maxv)) {
if (i > maxv) maxv = max(maxv, i);
if (a / i > maxv) maxv = max(maxv, a / i);
}
}
cout << maxv << " " << a - maxv << endl;
}
}
return 0;
}
C. Rational Ratio
题意: 定义一种交换:选择一段区间[l, r], 一次交换把[l, r]上的所有数改变位置(每个数字都必须改变位置),问把一个任意数列变成有序数列(a[i]=i),需要多少次这样的交换。数列个数2e5,a[i]~1e18
题解: 分析可知交换只有0,1,2三种情况,如果一开始就是有序的,输出0;一开始无序的段只有1个(无序的段意思是这个段内每个数字a[i] != i),那么输出1;否则输出2(如果无序的段有多个,可以把所有无序的段加上中间有序的看作一整个无序的段,然后通过1次交换,使得每个数字都不在原来的位置上,在进行一次交换都回到自己的位置上即可)
代码:
#include
using namespace std;
typedef long long LL;
int const N = 2e5 + 10;
LL a[N];
int T, n;
int main() {
cin >> T;
while (T--) {
cin >> n;
for (int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
int sorted = 1, rand_cnt = 0, contient = 1;
for (int i = 1; i <= n; ++i) {
if (a[i] != i) {
sorted = 0;
if (contient) {
rand_cnt ++;
contient = 0;
}
}
if (a[i] == i) {
contient = 1;
}
}
if (sorted) {
cout << 0 << endl;
continue;
}
else if (rand_cnt == 1) {
cout << 1<< endl;
continue;
}
else cout << 2<< endl;
}
return 0;
}
D. Omkar and Circle
题意: 给定n个数字,这n个数字都在一个圆上,每次可以选择3个数字,删掉这3个数字,把左右两边的数字的和赋值给中间的数字。最后圆上只会剩下一个数字,求剩下的数字的最大值。
题解: 选择3个数字,删掉这3个数字,把左右两边的数字赋值给中间的数字,这样的操作对于最后的答案来说,就是删掉中间那个数字。则n个数字,需要删掉n/2个数字,剩下n/2+1个数字。可以证明被删掉的数字一定是间隔的,因为如果不是间隔的,一定可以证明为间隔删更小。既然删掉n/2个数组,同时是间隔的,那么剩下的数字有两个数字是相邻的,其他都是间隔的。可以选择去枚举这两个相邻的数字,使用dp即可。li[i]维护从i往前,间隔的数字前缀和;re[i]维护从i往后,间隔的数字后缀和。不断枚举li[i]+re[i]的和最大值即可。
代码
#include
using namespace std;
typedef long long LL;
int const N = 2e5 + 10;
LL le[N], ri[N];
int n, a[N];
int main() {
cin >> n;
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
if (n == 1) {
cout << a[1] << endl;
return 0;
}
for (int i = 1; i <= n; ++i) {
if (i >= 2) le[i] = le[i - 2] + a[i];
else le[i] = a[i];
}
for (int i = n; i >= 1; --i) {
if (i <= n - 1) ri[i] = ri[i + 2] + a[i];
else ri[i] = a[i];
}
LL res = -1;
for (int i = 1; i <= n - 1; ++i) {
res = max(res, le[i] + ri[i + 1]);
}
res = max(res, le[n]);
cout << res << endl;
return 0;
}