Codeforces Round 968 (Div. 2) A~E1

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 k2

  • 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 1i<jk 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 1im1,将 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 1im1,将 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 1i<jn)是令人愉快的一对整数,当且仅当存在一个整数 k k k使得 i ≤ k < j i\le k\lt j ik<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 1i<jn)才是一对好的整数。

乌龟想给字符串 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×(n1)_i=1num1l_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=1num1l_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 1in,并将 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 1in 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 m1的每个 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_ik_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 1i<jn),使得 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(x1)+2y(y1)+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 pi+1k。否则,我们要考虑 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(i1)+2(ni)(ni1)

时间复杂度 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,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值