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,n−1] 中找到一个字符 a a a 作为名字 B B B ,那么将满足条件 B B B 的字典序比 A A A C C C 都要小或者等于
如果在 [ 2 , n − 1 ] [2,n-1] [2,n−1] 中找不到一个 a a a ,说明 [ 2 , n − 1 ] [2,n-1] [2,n−1] 中全是 b b b ,那么将这 [ 2 , n − 1 ] [2,n-1] [2,n−1]中所有的 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)=x1∣x2∣x3∣⋯∣xn , ∣ | ∣ 为 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 01011101111⇒01011110000⇒⋯⇒000000000
也就是说只要不做限制,最后将会变成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 ans 在
b.back()
后面得位都变成0,先找出等于 1 1 1得位,再让它于 1 1 1异或,自然结果就是 0 0 0了