Codeforces Round #789 (Div. 2) B2. Tokitsukaze and Good 01-String (hard version)

翻译:

这是这个问题的难解版本。两个版本之间的唯一区别是,较难的版本额外要求子段的最小数量。

Tokitsukaze有一个长度为𝑛的二进制字符串𝑠,仅由0和1组成,𝑛是偶数。

现在Tokitsukaze将𝑠划分为连续子段的最小数目,对于每个子段,每个子段中的所有位都是相同的。之后,如果所有子段的长度相等,则认为𝑠是好的。

例如,如果𝑠为“11001111”,则将其分为“11”、“00”和“1111”。它们的长度分别是2、2、4,都是偶数,所以“11001111”是好的。再例如,如果𝑠为“1110011000”,则将其分为“111”、“00”、“11”和“000”,它们的长度分别为3,2,2,3。显然,“1110011000”不好。

Tokitsukaze想通过改变𝑠中一些位置的值来使𝑠变得更好。具体来说,她可以执行任意次数的操作:将𝑠𝑖的值更改为'0'或'1'(1≤𝑖≤𝑛)。你能告诉她让𝑠好起来的最少操作次数吗?同时,她还想知道在所有操作次数最少的解中,𝑠可以被划分的子段的最小数目。

输入
第一个包含一个正整数𝑡(1≤𝑡≤10000)——测试用例的数量。

对于每个测试用例,第一行包含单个整数𝑛(2≤𝑛≤2⋅105)—𝑠的长度,保证𝑛为偶数。

第二行包含长度为𝑛的二进制字符串𝑠,仅由0和1组成。

保证𝑛在所有测试用例上的总和不超过2⋅105。

输出
对于每个测试用例,打印带有两个整数的一行—使𝑠正常运行的最小操作数,以及𝑠在具有最小操作数的所有解中可以划分的最小子段数。

例子
inputCopy
5
10
1110011000
8
11001111
2
00
2
11
6
100110
outputCopy
3 - 2
0 3
0 1
0 1
3个1
请注意
在第一个测试用例中,使𝑠变得良好的方法之一如下所示。

将𝑠3,𝑠6,𝑠7改为“0”,之后𝑠变成“1100000000”,它可以分为“11”和“00000000”,长度分别为2和8,其子段数为2。还有操作3次使𝑠变好的方法,如“1111110000”、“1100001100”、“1111001100”,它们的子段数分别为2、4、4。我们很容易发现,在所有具有最小操作数的解中,子段数的最小值是2。

在第二个、第三个和第四个测试用例中,𝑠最初是很好的,因此不需要操作。

思路:改变最小操作,可以很明显的得知,最小操作数就是判断相邻,因为n保证了是偶数,所以我们可以直接判断,两个相邻不同,我们可以修改其中的任意一个,所以我们可以改成00 或11,有我们将其定义为x,来作为可以任意改动过的标记。因为如果任意两个相同的拼到一起,就可以减少段数,因为段数如果不是全相同就是以2为底的。之后我们遍历标记,最后在记录一下连续段就可以了,最后防止是0取一个max。

代码:

#include <iostream>
#include <algorithm>
#include <string.h>
#include <string>
#include <math.h>
#include <stdio.h>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<tuple>
#include<numeric>
#include<stack>
using namespace::std;
typedef long long  ll;
int n,t;
inline __int128 read(){
    __int128 x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if(ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
inline void print(__int128 x){
    if(x < 0){
        putchar('-');
        x = -x;
    }
    if(x > 9)
        print(x / 10);
    putchar(x % 10 + '0');
}

string s;
void solv(){
    cin>>n>>s;
    ll an=0;
    for (int i =0; i<n; i+=2) {
        if (s[i]!=s[i+1]) {
            s[i]=s[i+1]='x';
            an++;
        }
    }
    printf("%lld ",an);
    an=0;
    char now='l';
    for (int i =0; i<n; i++) {
        if(s[i]=='x')continue;
        if (now!=s[i]) {
            now=s[i];
                an++;
        }
    }
    
    printf("%lld\n",max(an,1ll));
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(); cout.tie();
    cin>>t;
    while (t--) {
        solv();
    }
    return 0;
}
 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值