续2.25测试复盘

博客主要讲述了两个问题:1) 给定两个二进制序列,通过两次反转操作使它们相等的方法数;2) 字符串染色问题,判断是否能通过颜色交换使字符串升序排列。文章详细分析了题意,思路,并给出了AC代码。

描述

cocktail 刚刚在他的Ubuntu里发现了两个字符串 s 和 t!他想做以下操作恰好两次使得 s 变成 t。
操作是选择两个数字 l, r (1 ≤ l ≤ r ≤ n),将 si 变为 1-si 对于所有的 l ≤ i ≤ r。
cocktail 想知道有多少种方法来做到。
我们用如下规则来确定两种方式是否不同:

令 A = (a1, a2, a3, a4),其中 1 ≤ a1 ≤ a2 ≤ n, 1 ≤ a3 ≤ a4 ≤ n,是 cocktail 的一种操作方案。
令 B = (b1, b2, b3, b4),其中 1 ≤ b1 ≤ b2 ≤ n, 1 ≤ b3 ≤ b4 ≤ n,是 cocktail 的令一种操作方案。
A 和 B 是不同的当且仅当存在 k 满足 ak ≠ bk。

输入

输入有多组测试用例。第一行包含数字 T 表示测试用例数目。
对于每个测试用例,第一行包含一个数字 n (1 ≤ n ≤ 106),表示两个二进制序列的长度。
第二行包含一个字符串 s1s2…sn (si ∈ {0,1}),表示第一个二进制串。
第三行包含一个字符串 t1t2…tn (ti ∈ {0,1}),表示第二个二进制串。
保证所有测试用例 n 的总和不超过 107。

输出

对于每组测试用例,输出一个数字表示答案。

样例

3
1
1
0
2
00
11
5
01010
00111

输出

0
2
6

题意

有两个二进制序列,你可以选择两个区间(只能在s上),要求反转所选的这段序列,要使得两序列变成一样,求有多少这样的选择方案(考虑先后顺序,调换两个区间的反转顺序方案加一)。

思路

那天晚上做这个题的时候完全没看懂,没思路,就是没找到方案数有什么规律。
通过两端序列的相同与不同可以把一段序列分成两种不同颜色的组合,与另一相同的假如是绿色,不相同的是红色,我们要做的就是把两端序列中红色部分全部消除,由于我们只能选择两个区间,所以当红色部分达到三个及以上时就没有办法将他们都消除,当红色部分为两个时,即S可表示为 (绿)红绿红(绿),只能有三种翻法:三个一起翻再把中间那个翻回来;翻前两个,再翻后两个;翻第一个再翻第三个;把顺序倒一下就是6种。当红色部分只有一个时:因为我们翻两次嘛,所以第一次翻完之后保证红色部分还有一个就行,①把红色部分分两次翻有len-1种选法(len是红色部分的长度)②翻红色部分全部加绿色部分,绿色部分的选择有len-1+n种。加起来乘二就是2n-2种。当没有红色部分时:翻过去再返回来就行了,有n(n+1)/2种这里两次翻的是同一个区间所以就不用乘二了。

AC代码

#include<iostream>
#include<cstdio>
using namespace std;
char s[1000010],t[1000010];
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
       int n; 
       cin>>n;
       scanf("%s%s",s,t);
       int cnt=0;
       for(int i=0;i<n;i++)
         if((i==0||s[i-1]==t[i-1])&&s[i]!=t[i]) //算有多少个红色部分
             cnt++;
         if(cnt>2)
             cout<<"0"<<endl;
         else if(cnt==2)
             cout<<"6"<<endl;
         else if(cnt==1)
             cout<<(2*n-2)<<endl; 
         else   
             cout<<((long long)n*(n+1)/2)<<endl;//n<10^6可能爆int
     }
}

描述

给你一个由n个小写字母组成的字符串s。现在你必须给所有字符涂上两种颜色中的一种(每一个字符都只涂了一种颜色,相同的字母颜色可以相同也可以不相同)。着色后,可以交换字符串中颜色不同的任意两个相邻字符。您可以任意(可能是零)次执行这样的操作。目标是使字符串按升序排列。

输入

输入的第一行包含一个整数n(1≤n≤200)-字符串s的长度。
输入的第二行包含由n个小写字母组成的字符串s。

输出

如果无法为给定字符串上色,以便在上色后可以按交换序列排序达到字符串是按升序排列,请在第一行打印“NO”(不带引号)。
否则,在第一行打印“YES”,在第二行打印任何正确的颜色(颜色是由n个字符组成的字符串,如果第i个字符是第一种颜色,则第i个字符应为“0”,否则为“1”)。

样例

Input

9
abacbecfd

Output

YES
001010101

Input

8
aaabbcbb

Output

YES
01011011

Input

7
abcdedc

Output

NO

Input

5
abcde

Output

YES
00000

题意

给定一个字符串,用两种颜色(0,1)给每个字母染色,相邻的不同色的字母可以交换位置,问是否存在一种染色方式使得通过交换字母位置使该字符串变成有序字符串,存在则输出“YES”和染色方式,不存在则输出“NO”。

思路

相邻的不同色的字母才可以交换位置,最后又要求整个字符串有序,不难得出相同颜色的元素是有序的,因为只有两种颜色,所以只能有两个独立的有序序列存在,否则涂色失败。

AC代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
char s[220];
int a[220];
int main()
{
     int n;
     cin>>n;
     int res,m0=0,m1=0,flag=1;//m0为涂0色的最大值,吗为涂1色的最大值
     for(int i=0;i<n;i++)
     {
         cin>>s[i];
         res=s[i]-'a';
         if(res>=m0)
         {
            a[i]=0;//记录涂色方案
            m0=res;//更新0色序列最大值
         }
         else
         {
            if(res>=m1)
            {
               a[i]=1;
               m1=res;
            }
            else//即比m1小又比m0小,发现第三序列,涂色失败
            {
               flag=0;
               cout<<"NO"<<endl;
               break;
            }
         }
     }
    if(flag)
    {
        cout<<"YES"<<endl;
        for(int i=0;i<n;i++)
           cout<<a[i];
    }
}

描述

尼古拉最近才开始从事竞争性节目,但已经有资格进入一个著名的奥运会决赛。将会有n个参与者,其中一个是尼古拉。像任何好的奥林匹克运动会一样,它由两轮组成。厌倦了传统规则,解决最多问题的参与者获胜,组织者想出了不同的规则。

假设在第一轮中,参与者A排在第x位,在第二轮中,参与者A排在第y位。那么参与者A的总分是x+y之和。参与者A的总分是参与者(包括A)的总数,其总分小于或等于A的总分。注意,一些参与者可能最终会有一个共同的总分。同样重要的是要注意,在第一轮和第二轮中,没有两名参与者在同一个地点打成平手。换言之,从1到n的每一个i中,恰好有一个参与者在第一轮中获得了第i名,正好有一个参与者在第二轮中获得了第i名。

奥运会刚结束,尼古拉就被告知他在第一轮获得了第x名,在第二轮获得了第y名。尼古拉不知道其他参与者的结果,但他想知道,如果我们考虑到对他最有利和最不利的结果,他能占据的最小和最大位置是什么。请帮助尼古拉找到这个问题的答案。

输入

第一行包含整数t(1≤t≤100)-要解决的测试用例数。

下面的每一行都包含整数n,x,y(1≤n≤10^9,1≤x,y≤n)-奥林匹克运动会的参赛人数,尼古拉第一轮和第二轮的参赛地点。

输出

打印两个整数-尼古拉可能占据的最小和最大位置。

样例

Input

1
5 1 3

Output

1 3

Input

1
6 3 4

Output

2 6

题意

一共有 n 个人,进行两轮比赛,假设第一轮比赛的名次为 x ,第二轮的名次为 y ,那么这个人的得分为 x + y ,最后的排位会按照得分的严格升序排列,有一个人在第一轮的名次为 x ,第二轮的名次为 y ,问这个人最终最好的名次和最差的名次分别是多少

思路

先来看最坏排名,因为得分是升序排列三个人并列第一那三个人就都是第三,所以我们要让尽可能多的人和尼古拉同分(比它小的是可以但毕竟比他相同的容易凑一些,性价比高些),那么我们可以得到下面这样的比赛结果所有人的得分都一样都是x+y分。
在这里插入图片描述
所以最坏情况下尼古拉的排名就是这样,能够使得前面 x+y-1 个人的最终分数都等于 x + y,也就是包含这个人在内的x+y-1个人并列第 x + y - 1 。
再来看看最好情况下的排名,也就是让尽可能多的人分数大于尼古拉的x+y,我们需要分情况讨论一下,当x+y<=n时,我们将上图中的x+y-1改成n,那么每个人的得分都是n+1,都大于尼古拉的得分,所以尼古拉位列第一(没有比这更好的)。再看另一种情况x+y>n,这个时候肯定拿不了第一了,那就让第一的得分尽可能小(其他人的得分才会边大,尼古拉的排名才能靠前)所以就让前面的两次的排名一样比如第一名两次都让他第一,第二就两次都第二,然后忽略掉他们(因为他们把两次一样的都拿走了对后面的不会有影响),拿掉一个之后排名肯定对应的要往前移一位,那么尼古拉的排名就是x+y-1了,一直移直到x+y=n回到第一种情况,也就是说尼古拉能在剩下这些没移的里面排第一了,那么倒目前为止移了多少人到尼古拉前面呢,假设为k那么x-k+y-k=n-k;解得k=x+y-n。所以此时的最好排名就是x+y-n+1. 代码简单,这个思路巨难想到😔

AC代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
  	long long n,x,y;
  	cin>>n>>x>>y;
  	int worst,best;//最好最坏排名
  	worst=min(n,x+y-1);//防止最坏情况排名超过总人数
  	best=max(1LL,x+y-n+1);//是1最好,对1longlong处理一下,max要求两参数类型相同
  	if(best>n)
   	    best=n;//防止最坏情况排名超过总人数
  	cout<<best<<" "<<worst<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值