Codeforces Round #843 (Div. 2) A1-C

Codeforces Round #843 (Div. 2)

A1. Gardener and the Capybaras (easy version)

题目大意

现在有三个字符串 A A A , B B B , C C C ,这三个字符串只由 a , b a , b a,b 这两个字母构成

已知 B B B 满足以下条件之一: B B B 的字典序比 A A A C C C 都要小或者等于 , B B B 的字典序比 A A A C C C 都要大或者等于

现在给你一个字符串 Z Z Z,让你从 Z Z Z 中找出一个符合上述条件的任意 A A A, B B B, C C C 组合

思路/证明

既然是easy version, 那就简单点,直接暴力搜索+遍历判断

数据小就是任性

代码

#include<bits/stdc++.h>
using namespace std;
string put;
int total_ask;
bool Judge1(string one, string two){
    int length = min(one.size(), two.size());
    for(int temp = 0 ; temp < length ; temp++){
        if(one[temp] > two[temp])
            return 0;
    }
    if(one.size() > two.size()) return 0;
    return 1;
}
bool Judge2(string one, string two){
    int length = min(one.size(), two.size());
    for(int temp = 0 ; temp < length ; temp++){
        if(one[temp] < two[temp])
            return 0;
    }
    if(one.size() < two.size()) return 0;
    return 1;
}
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> total_ask;
    while(total_ask--){
        cin >> put;
        int flag = 1;
        for(int temp = 1 ; temp < put.size() - 1 && flag; temp++){
            for(int temp2 = temp+1 ; temp2 < put.size() ; temp2++){
                string a = put.substr(0,temp);
                string b = put.substr(temp,temp2-temp);
                string c = put.substr(temp2);
                if(Judge1(b,a) && Judge1(b,c)){
                    cout << a << " " << b << " " << c << "\n";
                    flag = 0;
                    break;
                }
                if(Judge2(b,a) && Judge2(b,c)){
                    cout << a << " " << b << " " << c << "\n";
                    flag = 0;
                    break;
                }
            }
        }
        if(flag) cout << ":(\n";
    }
    return 0;
}

A2. Gardener and the Capybaras (hard version)

题目大意

跟Easy Version一样,只有数据量变大了

思路/证明

我们先看条件

B B B 的字典序比 A A A C C C 都要小或者等于 , B B B 的字典序比 A A A C C C 都要大或者等于

其实除了这个,还有一个很特殊的条件:现在给你一个字符串 Z Z Z,让你从 Z Z Z 中找出一个符合上述条件的任意 A A A, B B B, C C C 组合

看到任意两个字,像极了高中数学填空压轴题不会 写的模样

我们完全可以找特殊情况:因为只有 B B B 是受控制的,那我们就找 B B B 的特殊情况,注意,在名字中,字典序最小的名字就是 a a a

所以我只需要在 [ 2 , n − 1 ] [2,n-1] [2,n1] 中找到一个字符 a a a 作为名字 B B B ,那么将满足条件 B B B 的字典序比 A A A C C C 都要小或者等于

如果在 [ 2 , n − 1 ] [2,n-1] [2,n1] 中找不到一个 a a a ,说明 [ 2 , n − 1 ] [2,n-1] [2,n1] 中全是 b b b ,那么将这 [ 2 , n − 1 ] [2,n-1] [2,n1]中所有的 b b b 作为名字 B B B , 那么将满足条件 B B B 的字典序比 A A A C C C 都要大或者等于

代码

#include<bits/stdc++.h>
using namespace std;
string put;
int total_ask;
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> total_ask;
    while(total_ask--){
        cin >> put;
        int left, right, flag = 0;
        for(int temp = 1 ; temp < put.size() - 1 ; temp++){
            if(put[temp] == 'a'){
                left = temp; right = temp+1;
                flag = 1;
            }
        }
        if(flag){
            string a = put.substr(0,left);
            string c = put.substr(right);
            cout << a << " a " << c << "\n";
            continue;
        }
        string b = put.substr(1,put.size()-2);
        cout << put[0] << " " << b << " " << put[put.size() - 1] << "\n";
    }
    return 0;
}

B. Gardener and the Array

题目大意

现在有一个数组 A = a 1 , a 2 , ⋯   , a n A = {a_1, a_2, \cdots , a_n} A=a1,a2,,an ,选出两个子序列 q q q , p p p

现在定义一个函数 f ( x ) f(x) f(x) x x x 为一个数组, f ( x ) = x 1 ∣ x 2 ∣ x 3 ∣ ⋯ ∣ x n f(x) = x_1 | x_2 | x_3 | \cdots | x_n f(x)=x1x2x3xn , ∣ | 为 OR操作

现在问是否存在两个子序列使得 f ( q ) = f ( p ) f(q) = f(p) f(q)=f(p)

如果存在输出 Y e s Yes Yes , 不存在输出 N o No No

注意输入的数据为二进制位,例如:第 i i i 行输入1,5,6 ,代表 a i = 110010 ( 二进制表示 ) a_i = 110010 (二进制表示) ai=110010(二进制表示)

即 第 i i i 行输入 p 1 , p 2 , p 3 , … , p n p_1, p_2, p_3, \dots , p_n p1,p2,p3,,pn ,则 a i = 2 p 1 + 2 p 2 + 2 p 3 + ⋅ + 2 p n a_i = 2^{p_1} + 2^{p_2} + 2^{p_3} + \cdot +2^{p_n} ai=2p1+2p2+2p3++2pn

思路/证明

我们首先要了解 OR操作的相关性质,以下图为例:

只要OR操作的某一个数的某一位是1,那么无论它与任何一个数进行OR操作,这个位都是1

所以可以想到我们先将所有的数进行OR操作,然后记录每个位被OR 1 的次数

之后我们遍历所有的输入的数据,如果某个数据是可以被替代的(也就是说他所提供的是1的位完全可以由其他数提供)

那么这个数据将是可有可无的

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int total_ask, total_num, num, step;
vector<int> info[N];
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> total_ask;
    while(total_ask--){
        cin >> total_num;
        for(int temp = 0 ; temp <= total_num ; temp++) info[temp].clear();
        //要用map, 如果用一个数组去记录,每次初始化将会耗费大量时间
        map<int,int> Count;
        for(int temp = 0 ; temp < total_num ; temp ++){
            cin >> num;
            for(int temp2 = 0 ; temp2 < num ; temp2++){
                cin >> step;
                info[temp].push_back(step);
                Count[step]++;
            }
        }
        int flag = 0;
        for(int temp = 0 ; temp < total_num ; temp++){
            int flag2 = 1;
            for(auto pr : info[temp]){
                if(Count[pr] == 1){
                    flag2 = 0;
                    break;
                }
            }
            if(flag2){flag = 1; break;}
        }
        puts(flag ? "Yes" : "No");
    }
    return 0;
}

C. Interesting Sequence

题目大意

给你一个数 n n n x x x ,问是否存在一个数 m m m 使得

n n n & \& & ( n + 1 ) (n+1) (n+1) & \& & ( n + 2 ) (n+2) (n+2) & \& & ( n + 3 ) (n+3) (n+3) & \& & ⋯ \cdots & \& & m m m = = = x x x

如果存在,输出最小的 m m m , 如果不存在,输出 − 1 -1 1

思路/证明

首先先稍微了解一下 与(and) 运算,当两个位都为1的时候,这个位才为1

可以观察到, n n n & \& & ( n + 1 ) (n+1) (n+1) & \& & ( n + 2 ) (n+2) (n+2) & \& & ( n + 3 ) (n+3) (n+3) 会不断把 n n n 后面全部变成0

也就是说假如 n n n = 01011101111 01011101111 01011101111 , 那将会有一下过程如果一直递增与下去

01011101111 ⇒ 01011110000 ⇒ ⋯ ⇒ 000000000 01011101111 \Rightarrow 01011110000 \Rightarrow \dots \Rightarrow 000000000 0101110111101011110000000000000

也就是说只要不做限制,最后将会变成0

那我们来把初始的 n n n 与最后的 t a r g e t target target 作比较 n n n = 10, t a r g e t target target = 8
1 0 1 0 1 0 0 0 1\quad0\quad1\quad0 \\ 1\quad0\quad0\quad0 10101000
可以看到对于10来说,第四位是要保护的位,第二位是要消除的位

所以如果要想有解,那么 t a r g e t target target位上所有的1, n n n上都要有,且要消除的最高位不能高于要保留的最低位

但注意,我们要寻找的m是要大于n的,所以消除的最高位不能大于等于要保留的最低位

因为需要的 m m m n n n大那么需要这个消除的位需要进1,这会使它的上一位取反。

注:因为代码涉及位运算,所以会有注解,方便我这种小白看懂…

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long int lli;
lli total_ask, num1, target;
signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> total_ask;
    while(total_ask--){
        cin >> num1 >> target;
        if((num1 & target) != target) {cout << "-1\n"; continue;}
        if(num1 == 0) {cout << "0\n"; continue;}
        vector<int> a, b;
        for(int temp = 0 ; temp < 60 ; temp++){
            if(num1 >> temp & 1){
                if(target >> temp & 1)
                    a.push_back(temp);
                else
                    b.push_back(temp);
            }
        }
        if(b.size() == 0) {cout << num1 << "\n"; continue;}
        if(a.size() != 0 && b.back() + 1 >= a[0]) {cout << "-1\n"; continue;}
        lli ans = num1;
        ans += 1LL << b.back();
        for(int temp = b.back() ; temp >= 0 ; temp--){
            if(ans >> temp & 1)
                ans ^= (1LL << temp);
        }
        cout << ans << "\n";
    }
    return 0;
}

注解

  • (num1 & target) != target

    主要是判断 是否 t a r g e t target target位上所有的1, n n n上都有

    比如 n u m 1 = 11001 num1 = 1 1 0 0 1 num1=11001 t a r g e t = 11000 target = 1 1 0 0 0 target=11000, 那么他们 & \& & 的值是 11000 1 1 0 0 0 11000

    但如果 t a r g e t = 10100 target = 1 0 1 0 0 target=10100, 那么他们 & \& &的值是 10000 1 0 0 0 0 10000

  • vector<int> a, b

    a a a用于存储要保留的位, b b b用于要消去的位, a[0]则是要保留的最低位, b.back() 则表示要删除的最高位

  • num1 >> temp & 1

    主要是判断num1的第temp位是几,举个例子:

    n u m 1 = 100110 num1 = 100110 num1=100110, 判断第五位, num >> 5 = 000010 000010 000010 , 在于 1 ( 000001 1(000001 1(000001)取 & \& & 0 0 0

  • for(int temp = b.back() ; temp >= 0 ; temp--){
        if(ans >> temp & 1)
           ans ^= (1LL << temp);
    }
    

    主要目的是让 a n s ans ansb.back() 后面得位都变成0,先找出等于 1 1 1得位,再让它于 1 1 1异或,自然结果就是 0 0 0

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yyym__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值