Codeforces Round #655 (Div. 2)

目录
  • 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;
}

E. Omkar and Last Floor

F. Omkar and Modes

你可能感兴趣的:(Codeforces Round #655 (Div. 2))