Codeforces Round #634 (Div. 3)

C - Two Teams Composing

题意

给定一组数,选择一些数将其数量相等的两个组,其中一个元素必须相同,另一个元素必须不同

问能分出来的最大的两个数组的长度是多少

题解

这题一开始想的是二分,本质上这道题是可以二分的,但是我的二分好像考虑的东西有点少,wa2在了400多个点上,转而还是写结论贪心

我们发现,如果数的种类数和最多那个数的个数相等的话,就说明我们得从最多的那个数中拿出来一个给数组为全不相等的那个,比如1 1 1 2 3,那么我们可以舍弃掉一个最多的数,让其能够组成1 1; 2 3

如果两者不相等,我们显然可以找到种类数与个数一一搭配的方案,也就是两者取min

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <numeric>
#include <sstream>
#include <unordered_map>
#define ll long long
#define ull unsigned long long
#define re return
#define pb push_back
#define Endl "\n"
#define endl "\n"
#define x first
#define y second
#define all(x) (x).begin(),(x).end()

using namespace std;

using PII = pair<int, int>;

const int N = 1e5 + 10;
const int M = 1e5 + 10;
const int mod = 1000000007;
const int INF = 0x3f3f3f3f;

int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};

int T;
int n;

void solve(){
    set<int> s;
    map<int, int> m;
    cin >> n;
    while(n--){
        int x;
        cin >> x;
        s.insert(x), m[x]++;
    }

    int maxx = 0;
    for(auto i : m){
        maxx = max(i.y, maxx);
    }
    int cnt = s.size();

    if(maxx == cnt){
        cout << maxx - 1 << endl;
    }
    else{
        cout << min(maxx, cnt) << endl;
    }
}

int main(){
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    T = 1;
    cin >> T;
    while(T--){
        solve();
    }
}

E1 - Three Blocks Palindrome (easy version)

题意

给定一组数,在其中找到一些子串,使得子串满足的这样的形式:xxxxyyyyxxxx

题解

一开始的时候想到了前后缀,盯着题目标记的只有26个数想了半天,想怎么存截止到i长度最大的呢,想了半天也没想出来

结果一看题解,我真是**

题目说了n最大2000

我们枚举y所在的区间的最大值,然后看看两边的能构成答案的最大值是多少,然后每次统计出来求答案即可

Code

#include <iostream>
#include <cstring>

using namespace std;

const int N = 1e5 + 10;

int n;
int a[N];
int pres[27][N];
int pree[27][N];

void solve(){
    memset(pres, 0, sizeof(pres));
    memset(pree, 0, sizeof(pree));
    cin >> n;
    for(int i = 1; i <= n; i++){
        cin >> a[i];
    }

    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= 26; j++){
            pres[j][i] = pres[j][i - 1];
        }

        pres[a[i]][i]++;
    }

    for(int i = n; i >= 1; i--){
        for(int j = 1; j <= 26; j++){
            pree[j][i] = pree[j][i + 1];
        }

	        pree[a[i]][i]++;
    }

    int ans = 0;

    for(int l = 1; l <= n; l++){
        for(int r = l; r <= n; r++){
            // cout << l << ' ' << r << endl;
            int x = 0, y = 0;
            for(int i = 1; i <= 26; i++){
                x = max(x, min(pres[i][l - 1], pree[i][r + 1]));
                y = max(y, pres[i][r] - pres[i][l - 1]);
            }
            // cout << endl;
            ans = max(ans, x * 2 + y);
        }
        // cout << endl;
    }

    cout << ans << endl;
}

int main(){
    int T;
    cin >> T;
    while(T--){
        solve();
        // cout << endl;
    }
}

E2 - Three Blocks Palindrome (hard version)

题意

与ez版本相比

数的种类增加至200个,数组长度增加到2e5

题解

我们回顾我们ez代码发现,我们每一次都枚举每一段的长度这是很浪费时间的,而且做的也是无用功

我们每次确定好左右两端的数字选择后,统计好这个数字出现的位置,选择两边相同的数目,看看中间选择哪一个数字组成的答案最大就可以了

Code

const int N = 2e5 + 10;

int n;
int a[N];
int pres[201][N];
// int pree[201][N];

void solve(){
    vector<int> v[201];

    cin >> n;
    for(int i = 1; i <= n; i++){
        cin >> a[i];
        v[a[i]].pb(i);
    }

    for (int i = 0; i <= n + 1; i++){
        for(int j = 1; j <= 200; j++){
            pres[j][i] = 0;
        }
    }

    int ans = 0;

    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= 200; j++)
        {
            pres[j][i] = pres[j][i - 1];
        }

        pres[a[i]][i]++;
        ans = max(ans, pres[a[i]][i]);
    }
    

    for (int b = 1; b <= 200; b++){
        int sz = v[b].size();
        for (int x = 1; x * 2 <= sz; x++){
            int l = v[b][x - 1] + 1;
            int r = v[b][sz - x] - 1;

            for (int a = 1; a <= 200; a++){
                int y = pres[a][r] - pres[a][l - 1];
                ans = max(ans, x * 2 + y);
            }
        }
    }

    cout << ans << endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值