A.Turtle and Good Strings(思维)
题意:
乌龟认为,如果存在一串字符串 t _ 1 , t _ 2 , … , t _ k t\_1,t\_2,\ldots,t\_k t_1,t_2,…,t_k( k k k是任意整数),且该字符串 s s s满足下列条件,那么该字符串 s s s就是一个好字符串:
-
k ≥ 2 k\ge 2 k≥2。
-
s = t _ 1 + t _ 2 + … + t _ k s=t\_1+t\_2+\ldots+t\_k s=t_1+t_2+…+t_k,其中 + + +表示连接操作。例如, abc = a + bc \texttt{abc}=\texttt{a}+\texttt{bc} abc=a+bc。
-
对于所有 1 ≤ i < j ≤ k 1\le i\lt j\le k 1≤i<j≤k, t _ i t\_i t_i的第一个字符不等于 t _ j t\_j t_j的最后一个字符。
乌龟得到一个由小写拉丁字母组成的字符串 s s s。请告诉他 s s s是否是一个好字符串!
分析:
直接判断第一个字符是否与最后一个字符相等即可。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N=100;
const int MOD=1000000007;
void solve(){
int n;
cin>>n;
string s;
cin>>s;
if(s[0] == s[n-1]){
cout<<"No"<<endl;
}
else{
cout<<"YES"<<endl;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;
cin>>T;
while(T--){
solve();
}
return 0;
}
B.Turtle and Piggy Are Playing a Game 2(贪心)
题意:
乌龟和小猪正在玩一个数列游戏。他们得到一个数列 a _ 1 , a _ 2 , … , a _ n a\_1,a\_2,\ldots,a\_n a_1,a_2,…,a_n,乌龟先来。乌龟和小猪轮流进行(因此,小乌龟做第一轮,小猪做第二轮,小乌龟做第三轮……)。
游戏过程如下
-
假设当前序列的长度为 m m m。如果是 m = 1 m=1 m=1,游戏结束。
-
如果游戏没有结束,轮到乌龟,那么乌龟必须选择一个整数 i i i,使得 1 ≤ i ≤ m − 1 1\le i\le m-1 1≤i≤m−1,将 a _ i a\_i a_i设为 max ( a _ i , a _ i + 1 ) \max(a\_i,a\_{i+1}) max(a_i,a_i+1),并删除 a _ i + 1 a\_{i+1} a_i+1。
-
如果游戏没有结束,轮到小猪,那么小猪必须选择一个整数 i i i,使得 1 ≤ i ≤ m − 1 1\le i\le m-1 1≤i≤m−1,将 a _ i a\_i a_i设置为 min ( a _ i , a _ i + 1 ) \min(a\_i,a\_{i+1}) min(a_i,a_i+1),并删除 a _ i + 1 a\_{i+1} a_i+1。
乌龟希望最终使 a _ 1 a\_1 a_1的值最大,而小猪希望最终使 a _ 1 a\_1 a_1的值最小。如果双方都以最优方式下棋,求 a _ 1 a\_1 a_1最终的值。
分析:
考虑贪心,想让元素尽可能大,那就每次移除最小的元素,反之就是每次移除最大的元素。
对数组进行排序,每次移除一个最大,移除一个最小,剩下的最后一个肯定就是数组中间的那个。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N=100;
const int MOD=1000000007;
void solve(){
int n;
cin>>n;
vector<int>a(n);
for(int i=0;i<n;i++){
cin>>a[i];
}
sort(a.begin(),a.end());
cout<<a[n/2]<<endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;
cin>>T;
while(T--){
solve();
}
return 0;
}
C.Turtle and Good Pairs(思维)
题意:
乌龟会给出一个由小写拉丁字母组成的字符串 s s s。
乌龟认为整数对 ( i , j ) (i,j) (i,j)( 1 ≤ i < j ≤ n 1\le i\lt j\le n 1≤i<j≤n)是令人愉快的一对整数,当且仅当存在一个整数 k k k使得 i ≤ k < j i\le k\lt j i≤k<j和以下两个条件都成立:
-
s _ k ≠ s _ k + 1 s\_k\ne s\_{k+1} s_k=s_k+1;
-
s _ k ≠ s _ i s\_k\ne s\_i s_k=s_i或 s _ k + 1 ≠ s _ j s\_{k+1}\ne s\_j s_k+1=s_j。
此外,乌龟认为当且仅当 s _ i = s _ j s\_i=s\_j s_i=s_j或 ( i , j ) (i,j) (i,j)是一对令人愉快的整数时,一对整数 ( i , j ) (i,j) (i,j)( 1 ≤ i < j ≤ n 1\le i\lt j\le n 1≤i<j≤n)才是一对好的整数。
乌龟想给字符串 s s s重新排序,从而使好的配对数达到最大。请帮帮他!
分析:
题目要求我们最大化好对的数量,考虑分类讨论一下哪些 ( i , j ) (i,j) (i,j)是好对。
-
若 S _ i = S _ j S\_i=S\_j S_i=S_j,那么 ( i , j ) (i,j) (i,j)是一个好对。
-
若 S _ i ≠ S _ j S\_i\neq S\_j S_i=S_j,且存在 k k k属于 ( i , j ) (i,j) (i,j)使得 S _ k ≠ S _ i S\_k\neq S\_i S_k=S_i且 S _ k ≠ S _ j S\_k\neq S\_j S_k=S_j,那么 ( i , j ) (i,j) (i,j)是一个好对。
可以把 S S S划分成若干个由相同字符组成的连续段,如果 ( i , j ) (i,j) (i,j)为好对,则 i i i所在的连续段和 j j j所在的连续段必然不相邻。令 n u m num num为字符串 S S S连续段的数量, l _ i l\_i l_i为第 i i i段的长度,那么好对数量应为 n × ( n − 1 ) 2 − ∑ _ i = 1 n u m − 1 l _ i × l _ i + 1 \frac{n\times (n-1)}{2}-\sum\_{i=1}^{num-1} l\_i\times l\_{i+1} 2n×(n−1)−∑_i=1num−1l_i×l_i+1,问题转化为令 ∑ _ i = 1 n u m − 1 l _ i × l _ i + 1 \sum\_{i=1}^{num-1} l\_i\times l\_{i+1} ∑_i=1num−1l_i×l_i+1最小。这样进行构造,令每一个字符不等于上一个字符,当只剩余一个字符时,全放在字符串最后即可。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const LL N = 5e5 + 5, P = 13331;
struct node{
LL id, num;
bool operator<(const node &u) const
{
return num < u.num;
}
};
void solve(){
LL n;
node l[26];
cin >> n;
char c;
for (LL i = 0; i < 26; i++)
l[i].id = i, l[i].num = 0;
for (LL i = 0; i < n; i++){
cin >> c;
l[c - 'a'].num++;
}
sort(l, l + 26);
LL st = 0, ed = 25;
while (l[st].num == 0)
st++;
LL cnt = 0;
while (cnt < n){
if (cnt % 2 == 0){
if (l[ed].num == 0){
ed--;
}
cout << (char)('a' + l[ed].id);
l[ed].num--;
cnt++;
}
else{
if (l[st].num == 0){
st++;
}
cout << (char)('a' + l[st].id);
l[st].num--;
cnt++;
}
}
cout << endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
LL T;
cin>>T;
while(T--){
solve();
}
return 0;
}
D1.Turtle and a MEX Problem (Easy Version)(思维)
题意:
在这个版本的问题中,你可以两次或更多次选择同一个整数。
有一天,乌龟在玩 n n n个序列。假设第 i i i个序列的长度是 l _ i l\_i l_i。那么第 i i i个序列是 a _ i , 1 , a _ i , 2 , … , a _ i , l _ i a\_{i,1},a\_{i,2},\ldots,a\_{i,l\_i} a_i,1,a_i,2,…,a_i,l_i。
小猪在乌龟玩耍的时候给乌龟出了一道难题。问题的陈述是:
-
一开始有一个非负整数 x x x。乌龟要对这个整数进行任意次数(可能是零次)运算。
-
在每次运算中,乌龟可以选择一个整数 i i i,使得 1 ≤ i ≤ n 1\le i\le n 1≤i≤n,并将 x x x设为 mex † ( x , a _ i , 1 , a _ i , 2 , … , a _ i , l _ i ) \text{mex}^{\dagger}(x,a\_{i,1},a\_{i,2},\ldots,a\_{i,l\_i}) mex†(x,a_i,1,a_i,2,…,a_i,l_i)。
-
乌龟被要求找出任意次数运算后 x x x的最大值。
乌龟毫不费力地解决了上述问题。他将 f ( k ) f(k) f(k)定义为当 x x x的初始值为 k k k时上述问题的答案。然后,小猪给了乌龟一个非负整数 m m m,并让乌龟找出 ∑ _ i = 0 m f ( i ) \sum\limits\_{i=0}^m f(i) ∑_i=0mf(i)的值(即 f ( 0 ) + f ( 1 ) + … + f ( m ) f(0)+f(1)+\ldots+f(m) f(0)+f(1)+…+f(m)的值)。不幸的是,他无法解决这个问题。请帮帮他!
† mex ( c _ 1 , c _ 2 , … , c _ k ) ^{\dagger}\text{mex}(c\_1,c\_2,\ldots,c\_k) †mex(c_1,c_2,…,c_k)被定义为在序列 c c c中不出现的最小非负整数 x x x。例如, mex ( 2 , 2 , 0 , 3 ) \text{mex}(2,2,0,3) mex(2,2,0,3)是 1 1 1, mex ( 1 , 2 ) \text{mex}(1,2) mex(1,2)是 0 0 0。
分析:
对于每个序列,我们最多操作两次,可以得到每个序列的第二个 m e x 2 mex2 mex2。那么当 x x x小于最大的 m e x 2 mex2 mex2时,将其变成 m e x 2 mex2 mex2,否则使用 x x x本身。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const LL N=100;
const LL MOD=1000000007;
void solve(){
LL n,m;
cin>>n>>m;
vector<pair<LL,LL>> a(n);
LL ma=0;
for(LL i=0;i<n;i++){
LL len;
cin>>len;
vector<LL> t(len+10);
for(LL j=0;j<len;j++){
LL x;
cin>>x;
if(x<len+10)
t[x]=1;
}
LL c=1;
for(LL j=0;j<=len+5 && c<=2;j++){
if(!t[j] && c==1){
a[i].first=j;
c++;
}
else if(!t[j]){
c++;
a[i].second=j;
ma = max(ma,j);
break;
}
}
}
LL ans=0;
if (m<=ma){
ans+=(m+1)*ma;
}
else{
ans+=(ma+1)*ma;
ans+=(ma+1+m)*(m-ma)/2;
}
cout<<ans<<endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
LL T;
cin>>T;
while(T--){
solve();
}
return 0;
}
D2.Turtle and a MEX Problem (Hard Version)(思维)
题意:
在这个版本的问题中,你不能两次或多次选择同一个整数。
有一天,乌龟在玩 n n n个序列。假设第 i i i个序列的长度为 l _ i l\_i l_i。那么第 i i i个序列是 a _ i , 1 , a _ i , 2 , … , a _ i , l _ i a\_{i,1},a\_{i,2},\ldots,a\_{i,l\_i} a_i,1,a_i,2,…,a_i,l_i。
小猪在乌龟玩耍的时候给乌龟出了一道难题。问题的陈述是
-
一开始有一个非负整数 x x x。乌龟要对这个整数进行任意次数(可能是零)的运算。
-
在每一次运算中,乌龟可以选择 i i i中的一个整数**{ 1 ≤ i ≤ n 1\le i\le n 1≤i≤n和 i i i**},并将 x x x设为 mex † ( x , a _ i , 1 , a _ i , 2 , … , a _ i , l _ i ) \text{mex}^{\dagger}(x,a\_{i,1},a\_{i,2},\ldots,a\_{i,l\_i}) mex†(x,a_i,1,a_i,2,…,a_i,l_i)。
-
乌龟被要求找出任意次数运算后 x x x的最大值。
乌龟毫不费力地解决了上述问题。他将 f ( k ) f(k) f(k)定义为当 x x x的初始值为 k k k时上述问题的答案。
然后,小猪给了乌龟一个非负整数 m m m,让乌龟找出 ∑ _ i = 0 m f ( i ) \sum\limits\_{i=0}^m f(i) ∑_i=0mf(i)的值(即 f ( 0 ) + f ( 1 ) + … + f ( m ) f(0)+f(1)+\ldots+f(m) f(0)+f(1)+…+f(m)的值)。不幸的是,他无法解决这个问题。请帮帮他!
† mex ( c _ 1 , c _ 2 , … , c _ k ) ^{\dagger}\text{mex}(c\_1,c\_2,\ldots,c\_k) †mex(c_1,c_2,…,c_k)被定义为在序列 c c c中不出现的最小非负整数 x x x。例如, mex ( 2 , 2 , 0 , 3 ) \text{mex}(2,2,0,3) mex(2,2,0,3)是 1 1 1, mex ( 1 , 2 ) \text{mex}(1,2) mex(1,2)是 0 0 0。
分析:
本题要求在一次操作中每个序列最多使用一次。 对于第 i i i个序列,我们建一条有向边 u _ i − > v _ i u\_i->v\_i u_i−>v_i。一次操作 x x x可以沿着一条出边移动,或者移动到一个任意点 u u u,并断开它的出边。
先倒序处理一下,令 b _ i b\_i b_i等于 i i i能够到达的最大值。可知一个 x x x的答案至少是 f _ x f\_x f_x,所有 x x x的答案至少是 max _ i = 1 n { u _ i } \max\_{i=1}^{n} \{u\_i\} max_i=1n{u_i};对于所有出度大于 1 1 1的 i i i, x x x的答案至少是 f _ i f\_i f_i。
因此实现步骤为:倒序处理 f _ i f\_i f_i、记录每个点的出度、记录度大于 1 1 1的最大的 f _ i f\_i f_i、记录最大的 u _ i u\_i u_i。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const LL N=100;
const LL MOD=1000000007;
void solve(){
LL n,m;
cin>>n>>m;
vector<pair<LL,LL>> a(n);
LL ma=0;
for (LL i=0;i<n;i++){
LL len;
cin>>len;
ma=max(ma,len);
vector<LL>t(len+10);
for (LL j=0;j<len;j++){
LL x;
cin>>x;
if(x<len+10)
t[x]=1;
}
LL c=1;
for(LL j=0;j<=len+5 && c<=2;j++){
if(!t[j] && c==1){
a[i].first=j;
c++;
}
else if(!t[j]){
c++;
a[i].second=j;
break;
}
}
}
sort(a.begin(), a.end(),
[&](pair<LL,LL> a, pair<LL,LL> b){
return a.first > b.first;
});
LL mma = 0;
vector<LL> b(ma + 10);
vector<LL> chu(ma + 10);
for (auto [x, y] :a){
mma=max(mma,x);
LL t=max(y, b[y]);
b[x]=max(b[x], t);
chu[x]++;
}
LL t2=-1;
for(auto [x, y] :a){
if (chu[x]>=2 && b[x]>t2){
t2=b[x];
}
}
LL ans=0;
for(LL i=0;i<=ma+5 && i<=m;i++){
b[i]=max({i,b[i],t2,mma});
ans+=b[i];
}
if(m>ma+5){
ans+=(ma+6+m)*(m-ma-5)/2;
}
cout<<ans<<endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
LL T;
cin>>T;
while(T--){
solve();
}
return 0;
}
E1.Turtle and Inversions (Easy Version)(动态规划)
题意:
在简易版中, 1 1 1至 m − 1 m-1 m−1的每个 i i i都有 m m m和 r _ i < l _ i + 1 r\_i\lt l\_{i+1} r_i<l_i+1的约束条件。
乌龟给出了 m m m个间隔 [ l _ 1 , r _ 1 ] , [ l _ 2 , r _ 2 ] , … , [ l _ m , r _ m ] [l\_1,r\_1],[l\_2,r\_2],\ldots,[l\_m,r\_m] [l_1,r_1],[l_2,r_2],…,[l_m,r_m]。他认为,如果每个区间( l _ i ≤ k _ i < r _ i l\_i\le k\_i\lt r\_i l_i≤k_i<r_i)都存在一个整数 k _ i k\_i k_i,并且从 1 1 1到 m m m的每个整数 i i i都有 a _ i = max _ j = l _ i k _ i p _ j , b _ i = min _ j = k _ i + 1 r _ i p _ j a\_i=\max\limits\_{j=l\_i}^{k\_i}p\_j,b\_i=\min\limits\_{j=k\_i+1}^{r\_i}p\_j a_i=max_j=l_ik_ip_j,b_i=min_j=k_i+1r_ip_j,那么下面的条件成立,那么 p p p这个排列就是有趣的:
max _ i = 1 m { a _ i } < min _ i = 1 m { b _ i } \max\limits\_{i=1}^m \{a\_i\}\lt \min\limits\_{i=1}^m \{b\_i\} max_i=1m{a_i}<min_i=1m{b_i}
乌龟希望你计算出长度为 n n n的所有有趣排列的最大反转数,或者告诉他是否没有有趣的排列。
排列 p p p的倒置是一对整数 ( i , j ) (i,j) (i,j)( 1 ≤ i < j ≤ n 1\le i\lt j\le n 1≤i<j≤n),使得 p _ i > p _ j p\_i\gt p\_j p_i>p_j。
分析:
把所有的数分成两类:小数( 0 0 0)和大数( 1 1 1),使得任何一个小数都小于任何一个大数。这样一个排列组合就可以转化为一个 01 01 01序列。
当且仅当存在一种将所有数分成两类的方法,使得在每个给定的区间 [ l , r ] [l,r] [l,r]中,所有的 0 0 0都在 1 1 1之前,并且在该区间中至少有一个 0 0 0和一个 1 1 1时,这种排列才是有趣的。这样的 01 01 01序列称为有趣序列。
如果有趣的 01 01 01序列是固定的,我们可以贪心地将所有 0 0 0按降序排列,将所有 1 1 1按降序排列。设 0 0 0的个数为 x x x, 1 1 1的个数为 y y y。设 z z z是下标对 ( i , j ) (i,j) (i,j)的个数,其中第 i i i个数是 1 1 1且第 j j j个数是 0 0 0(这样的下标对称为 10 10 10对)。那么,逆序对的最大值是 x ( x − 1 ) 2 + y ( y − 1 ) 2 + z \frac{x(x-1)}{2}+\frac{y(y-1)}{2}+z 2x(x−1)+2y(y−1)+z。
在这个版本中,区间是不相交的,因此可以直接应用 D P DP DP。假设 f _ i , j f\_{i,j} f_i,j代表从 1 1 1到 i i i的有 j j j个数字 1 1 1的所有数中 10 10 10对的最大数量。至于转移,如果 i i i是一个区间的左端点,而这个区间的右端点是 p p p,那么我们可以枚举出该区间转移的 0 0 0个数为 k k k, 1 1 1个数为 p − i + 1 − k p-i+1-k p−i+1−k。否则,我们要考虑 i i i是 0 0 0还是 1 1 1。
答案是 max _ i = 0 n f _ n , i + i ( i − 1 ) 2 + ( n − i ) ( n − i − 1 ) 2 \max\limits\_{i=0}^n f\_{n,i}+\frac{i(i-1)}{2}+\frac{(n-i)(n-i-1)}{2} max_i=0nf_n,i+2i(i−1)+2(n−i)(n−i−1)。
时间复杂度 O ( n 2 + m ) O(n^2+m) O(n2+m)。
代码:
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 5050;
int n, m, f[maxn][maxn], a[maxn], b[maxn];
void solve() {
scanf("%d%d", &n, &m);
for (int i = 0; i <= n + 1; ++i) {
for (int j = 0; j <= n + 1; ++j) {
f[i][j] = -1e9;
}
a[i] = 0;
}
for (int i = 1, l, r; i <= m; ++i) {
scanf("%d%d", &l, &r);
a[l] = r;
}
f[0][0] = 0;
for (int i = 1; i <= n; ++i) {
if (a[i]) {
int p = a[i];
for (int j = 0; j < i; ++j) {
for (int k = 1; k <= p - i; ++k) {
f[p][j + k] = max(f[p][j + k], f[i - 1][j] + (p - i + 1 - k) * j);
}
}
i = p;
} else {
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 ans = 0;
for (int i = 0; i <= n; ++i) {
ans = max(ans, f[n][i] + i * (i - 1) / 2 + (n - i) * (n - i - 1) / 2);
}
printf("%d\n", ans);
}
int main(){
int T;
scanf("%d",&T);
while(T--){
solve();
}
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。