A. Turtle and Good Strings

根据题意可知,一个好的字符串一定可以拆成两个满足条件的字符串,所以只需要令\(k=2\)进行操作即可

code:

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
#include <bits/stdc++.h>

#define int long long
#define x first
#define y second
#define endl '\n'

using namespace std;

typedef pair<int, int> PII;

const int N = 200010;

int T;
int n;
string s;

signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);

cin >> T;
while (T--)
{
cin >> n >> s;

if (n >= 2 && s[0] != s.back()) cout << "YES" << endl;
else cout << "NO" << endl;
}

return 0;
}

B. Turtle and Piggy Are Playing a Game 2

转化题意大概是\(Turtle\)\(Piggy\)轮流删除序列中的数,\(Turtle\)要最大化\(a_1\)的值,所以每次轮到他操作时一定会删除序列中最小的数,\(Piggy\)要最小化\(a_1\),所以每次他会删除序列中最大的数

code:

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
#include <bits/stdc++.h>

#define int long long
#define x first
#define y second
#define endl '\n'

using namespace std;

typedef pair<int, int> PII;

const int N = 200010;

int T;
int n;
int a[N];

signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);

cin >> T;
while (T--)
{
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];

sort(a, a + n);

cout << a[n / 2] << endl;
}

return 0;
}

C. Turtle and Good Pairs

构造,可以观察到相同的字母相邻的个数越少,好整数的对数越多

code:

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
#include <bits/stdc++.h>

#define int long long
#define x first
#define y second
#define endl '\n'

using namespace std;

typedef pair<int, int> PII;

const int N = 200010;

int T;
int n;
string s;
int c[26];

signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);

cin >> T;
while (T--)
{
memset(c, 0, sizeof c);

cin >> n >> s;;

for (int i = 0; i < n; i++) c[s[i] - 'a']++;

int maxv = 0;
for (int i = 0; i < 26; i++) maxv = max(maxv, c[i]);

string res;
for (int i = 0; i < n; i++)
{
char op = ' ';
for (int i = 0; i < 26; i++)
{
if (!c[i]) continue;
if (c[i] == maxv)
{
if (res.size() && (char)(i + 'a') == res.back()) op = (char)(i + 'a');
else res += (char)(i + ' a');
c[i]--;
}
}
if (op != ' ') res += op;
maxv--;
}
cout << res << endl;
}

return 0;
}

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
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
#include <bits/stdc++.h>

#define int long long

using namespace std;

const int N = 200010;

int T;
int n, m;
vector<int> a[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 maxv = 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) mex++;

maxv = max(maxv, mex);
}

int res = 0, x = max((int)0, m - maxv);
for (int i = 0; i <= min(maxv, m); i++) res += maxv;
res += (maxv + 1 + m) * x / 2;

cout << res << endl;

for (int i = 0; i < n; i++) a[i].clear();
}

return 0;
}

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
#include <bits/stdc++.h>

#define int long long
#define x first
#define y second

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
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
#include <bits/stdc++.h>

#define int long long

using namespace std;

const int N = 5010;

int T;
int n, m;
int ne[N];
int f[N][N];

signed main()
{
cin >> T;
while (T--)
{
cin >> n >> m;
for (int i = 0; i < m; i++)
{
int l, r;
cin >> l >> r;
ne[l] = r;
}

for (int i = 0; i <= n; i++)
for (int j = 0; j <= n; j++)
f[i][j] = -1e9;

f[0][0] = 0;
for (int i = 1; i <= n; i++)
{
if (ne[i])
{
int r = ne[i];
for (int j = 0; j < i; j++)
for (int k = 1; k <= r - i; k++)
f[r][j + k] = max(f[r][j + k], f[i - 1][j] + (r - i + 1 - k) * j);
i = r;
continue;
}
for (int j = 0; j <= i; j++)
{
f[i][j] = f[i - 1][j] + j;
if (j) f[i][j] = max(f[i][j], f[i - 1][j - 1]);
}
}

int res = 0;
for (int i = 0; i <= n; i++)
res = max(res, f[n][i] + i * (i - 1) / 2 + (n - i) * (n - i - 1) / 2);

cout << res << endl;

for (int i = 1; i <= n; i++) ne[i] = 0;
}

return 0;
}