Codeforces Round 968 (Div. 2) 题解
A. Turtle and Good Strings
根据题意可知,一个好的字符串一定可以拆成两个满足条件的字符串,所以只需要令\(k=2\)进行操作即可
code:
1 |
|
B. Turtle and Piggy Are Playing a Game 2
转化题意大概是\(Turtle\)和\(Piggy\)轮流删除序列中的数,\(Turtle\)要最大化\(a_1\)的值,所以每次轮到他操作时一定会删除序列中最小的数,\(Piggy\)要最小化\(a_1\),所以每次他会删除序列中最大的数
code:
1 |
|
C. Turtle and Good Pairs
构造,可以观察到相同的字母相邻的个数越少,好整数的对数越多
code:
1 |
|
D1. Turtle and a MEX Problem (Easy Version)
首先处理出每个序列不添加\(x\)的\(mex1\),以及加上\(x\)后的\(mex2\)
由于每个序列可以被重复使用,手动模拟可以发现,对于任何整数\(x\),都可以在多次操作后变成任何\(mex1\)和\(mex2\),由于\({max(mex1)}<={max(mex2)}\),所以求出\({max(mex2)}\)即可,用\(mex\)来表示
对于\(f(0)···f({min(mex, m)})\),值都为\(mex\),大于\({min(mex, m)}\)的值为它本身,用等差数列求和公式来计算
code:
1 |
|
D2. Turtle and a MEX 问题 (Hard Version)
简单与困难版本唯一的区别是在计算\(f(i)\)时,每个序列只能操作一次
可以考虑对每对\(mex1\)和\(mex2\)建有向边,由于每对\(mex2\)一定大于\(mex1\),因此可以按值从大到小进行逆序\(dp\),求出每个\(x\)在图中可以转移到的最大值\(f(x)\),以及所有\(x\)都可以转移到的最大值\(mex\),最后对于每个\(x\),\(f(x)={max(f(x),x,mex)}\)
对于\(mex\),假设遍历到\(i\),若\(h[i].size()>=2\),代表着\(i\)作为\(mex1\)对应着至少两个\(mex2\),因此对于任意\(x\),可以先转移到\(i\),再转移到\(f(i)\),因此\(mex={max(mex,f(i))}\),如果\(h[i].size()<2\),那么对于任意\(x\),转移到\(i\)后无法再转移到\(f(i)\) 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
using namespace std;
const int N = 200010;
int T;
int n, m;
vector<int> a[N], h[N];
int f[N];
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> T;
while (T--)
{
cin >> n >> m;
for (int i = 0; i < n; i++)
{
int l;
cin >> l;
for (int j = 0; j < l; j++)
{
int x;
cin >> x;
a[i].push_back(x);
}
}
int mex1 = 0, mex2 = 0;
for (int i = 0; i < n; i++)
{
sort(a[i].begin(), a[i].end());
int mex = 0, flag = -1;
for (int j = 0; j < a[i].size(); j++)
{
if (a[i][j] > mex && flag == -1) flag = mex++;
if (a[i][j] == mex) mex++;
}
if (flag == -1) flag = mex++;
mex1 = max(mex1, flag), mex2 = max(mex2, mex);
h[flag].push_back(mex);
}
for (int i = 0; i <= mex2; i++) f[i] = i;
for (int i = mex2; i >= 0; i--)
{
for (int j = 0; j < h[i].size(); j++)
f[i] = max(f[i], f[h[i][j]]);
if (h[i].size() >= 2) mex1 = max(mex1, f[i]);
}
int res = (min(m, mex2) + 1 + m) * (m - min(m, mex2)) / 2;
for (int i = 0; i <= min(m, mex2); i++) res += max(f[i], mex1);
cout << res << endl;
for (int i = 0; i <= max(n, mex2); i++)
{
a[i].clear();
h[i].clear();
}
}
return 0;
}
E1. Turtle and Inversions (Easy Version)
把\(1\)到\(n\)所有的数分成小数和大数,并且任意小数小于任意大数,因此可以把小数当成\(0\),大数当成\(1\),转换成一个01序列问题
对于一个有趣的序列,在每一个区间中,所有的\(0\)都在\(1\)前面
为了使有趣的序列中逆序对的数量最大,应该从大到小放\(1\),从小到大放&0&,假设构造的序列有\(a\)个\(1\),\(b\)个\(0\),\(c\)个\(10\)对,那么该序列的逆序对个数为\(\frac{a(a-1)}{2} + \frac{b(b-1)}{2} + c\)
可以用dp去维护\(10\)对的个数,设\(f(i,j)\)为前\(i\)个数,有\(j\)个\(1\),能构成\(10\)对的最大个数,需要注意的是如果\(i\)是一个区间的左端点,那么应该只对其右端点进行转移
code:
1 |
|