Educational Codeforces Round 148 (Rated for Div. 2)题解 A B C

196 阅读3分钟

分享昨天晚上Codeforces一场比赛的题解

A New Palindrome

#题意

给定一个由小写字母组成的回文串, 你需要判断是否可以重新排列字母,使之成为与原字符串不同的新回文串。

#分析

既然是回文串,那么左右两边是对称的,只需考虑一边即可。 重新排列字母,且与原来字符串不同,显然相同字母交换顺序是没有意义的, 那只需要找出回文串的一边包含多少个不同字母,大于1即可。

#代码

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int N = 60;
char letters[N];

void solve(){
    string str;
    cin >> str;
    int sz = str.length();
    int cnt = 0;
    memset(letters, '\0', sizeof(letters));
    for(int i = 0; i < sz / 2; ++i){
        letters[str[i] - 'a']++;
    }
    for(int i = 0; i <= 25; ++i) {
        if(letters[i] != 0)cnt++;
    }
    if(cnt > 1)cout << "YES" << "\n";
    else cout << "NO" << "\n";
}

signed main(){
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int _; cin >> _;
    while(_ --){
        solve();
    }
    return 0;
}

B Maximum Sum

#题意

给定一个数组,每个元素都不同。你被规定进行k次操作,操作如下:

1.选出数组中最小的两个数,将它们删除

2.选出数组中最大的一个数,将其删除

每次操作只能选择其中一种,求出剩余数的最大和。

#分析

常规的想法应该是贪心,每一次将最小两个数的和与最大数比大小,然后删去较小值。 但是这样无法求出最优解,我们需要换一种思路。

我们可以将操作次数枚举:指定需要进行k次操作,其中包含i次操作1,那么操作2即为k-i次。 通过枚举我们可以知道每一次操作后的结果,用一个前缀和来维护,接着找出最大值即可。

#代码

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int N = 2e5 + 9;
int nums[N];
int presum[N];

void solve(){
    int n, k; cin >> n >> k;
    int sum = 0;
    for(int i = 1; i <= n; ++i){
        cin >> nums[i];
    }
    sort(nums + 1, nums + 1 + n);
    for(int i = 1; i <= n; ++i){
        presum[i] = presum[i - 1] + nums[i];
    }
    int ans = 0;
    for(int i = 0; i <= k; ++i){//枚举操作1
        int l = 2 * i;
        int r = n - (k - i);
        ans = max(ans, presum[r] - presum[l]);
    }
    cout << ans << '\n';
}

signed main(){
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int _; cin >> _;
    while(_--){
        solve();
    }
    return 0;
}

C Contrast Value

#题意

给定一个a数组,定义一个值contract为a1a2+a2a3+...+an1an|a1 - a2|+|a2 - a3|+...+|a{n-1} - an|,通过a数组得到一个b数组,满足以下条件:

1.b非空,至少有一个元素

2.b是a的一个子序列

3.b的contract值与a相等

需要求得b序列的最小长度。

#分析

通过式子我们可以发现,当一串数值是单调的,这串数值的contract值可以直接由头尾元素算得,中间元素不影响大小。例如1、3、6、8算出的值和1、8的效果是一样的。

这样我们只需要找到改变单调性的节点即峰值点,再算上首尾节点即可。

#代码

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int N = 3e5 + 9;
int a[N], p[N];

void solve(){
    int n; cin >> n;
    for(int i = 1; i <= n; ++i){
        cin >> a[i];
    }
    int ans = 1;
    //首先计入最后一个点
    //若序列单调,则中间值可以省略
    int tag = -1;
    //表示单调增->1或单调减->0
    //一开始不知道单调性,设为-1
    for(int i = 2; i <= n; ++i){
        if(a[i] == a[i - 1])continue;
        //当不确定单调性时,不知道第一个点从何取起
        //若第二个数和第一个数相等,则不作为转折点(不影响起点)
        //若不相等,第一个数即为起点,得ans++
        
        if(tag == -1 && a[i] != a[i - 1])ans++;
        if(tag == 0 && a[i] > a[i - 1])ans++;
        if(tag == 1 && a[i] < a[i - 1])ans++;
        tag = a[i] > a[i - 1] ? 1 : 0;
    }
    cout << ans << '\n';
}

signed main(){
    int _; cin >> _;
    while(_ --){
        solve();
    }
    return 0;
}