Codeforces Round #793 (Div. 2)

Codeforces Round #793 (Div. 2)

C

题意

给定一个长度为 \(n\) 的序列 \(a\) ,它的反串是 \(a'\)

现在重排这个 \(a\)

最大化 \(min(LIS(a),LIS(a'))\)

思路

一开始一直在想怎么构造出来,最后发现还是从 “对答案的贡献” 的角度分析···

维护两个集合,分别为构成正串 \(a\)\(LIS\) 的集合 \(s1\) ,和构成正串 \(a\)\(LCS\) 的集合 \(s2\)

我们要最大化 \(min(LIS(a),LIS(a'))\) 。就不能让任意一方格外小,所以我们依次维护 \(s1,s2\)

可以发现对于序列中一个数字 \(x\) ,如果这个数字出现的次数大于 \(2\) ,就不会再对答案有贡献。比如,\([1,2,2,2,3,4]\) 有, \(s1 = {1,2,3}\)\(s2 ={2,4}\) 。第三个 \(2\) 已经不会再对答案有贡献。

进而的,我们可以将数字分为 仅出现一次出现大于一次 的两类。第二类如上,会对答案造成 "加一的贡献"。第一类由于只能放入其中一个集合中,每两个第一类数字加入 \(s1\)\(s2\) 才会对答案有 “加一的贡献”。这可以等价于对答案有 "0.5的贡献"。

特别的,对于第一类数字,可以存在一个数字 \(y\) 复用 使得这个 \(y\) 的贡献变为 \(1\)。比如 序列 \([1 ,1, 4, 5, 2, 4, 5, 5, 4, 4]\) 最后的最优构造序列是 \([1 ,4, 4, 5, 4, 2, 1, 4, 5, 5]\) 。其中 \(s1 = 1,2,4,5\)\(s2 = 1,2,4,5\) 。这里面的 \(2\) 得到了复用。

也就是第一类 “0.5的贡献”变为了 \(1\)

因此最后答案应该是 第一类数贡献的上取整加上第二类数的贡献

代码非常简单。

#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;

void solve()
{    
    int N; cin >> N;
    map<int,int> mp;
    for(int i = 0;i < N;i ++) {
        int t; cin >> t;
        mp[t] ++;
    }
    int x = 0, y = 0;
    for(auto [v,c] : mp) {
        if(c == 1) x ++;
        else y ++;
    }
    cout << y + (x + 1) / 2 << endl;
    /* 
        4
        1 4 4 5 4 2 1 4 5 5

        5 4 2 1
        1 2 4 5
     */
    
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);

    int T;cin>>T;
    while(T--)
        solve();

    return 0;
}
posted @ 2022-05-23 13:49  Mxrurush  阅读(70)  评论(0编辑  收藏  举报