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;
}
标签:793,int,s2,Codeforces,long,贡献,LIS,Div,define 来源: https://www.cnblogs.com/Mxrush/p/16300928.html